spring
一、介绍
1、关于spring
spring是一个轻量级容器框架,用来管理web层、业务层、dao层,维护各个层之间的关系,将对象之间的依赖关系交由spring来控制,减少层与层之间的耦合,spring的核心功能是IOC和AOP。
2、缺点
spring本是一个轻量级的容器框架,主要包含IOC和AOP,用来管理各种bean,但是现在变得过于臃肿,包含有各种各样的东西,springMVC、spring Cloud等。还有就是spring的配置过于繁杂,感觉比敲代码还麻烦,需要记住各种配置。
3、引入
web项目要使用spring框架,需要注册监听器,在web.xml文件中配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframe.web.context.ContextLoaderListener</listener-class>
</listener>
二、IOC
1、关于bean
1)bean
bean:bean是特殊的java类,可以理解为可复用的组件。
属性 | 描述 |
---|---|
class | 这个属性是强制性的,并且指定用来创建 bean 的 bean 类。 |
name | 这个属性指定唯一的 bean 标识符。在基于 XML 的配置元数据中,你可以使用 ID 和/或 name 属性来指定 bean 标识符。 |
scope | 这个属性指定由特定的 bean 定义创建的对象的作用域,它将会在 bean 作用域的章节中进行讨论。 |
constructor-arg | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
properties | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
autowiring mode | 它是用来注入依赖关系的,并会在接下来的章节中进行讨论。 |
lazy-initialization mode | 延迟初始化的 bean 告诉 IoC 容器在它第一次被请求时,而不是在启动时去创建一个 bean 实例。 |
initialization | 在 bean 的所有必需的属性被容器设置之后,调用回调方法。它将会在 bean 的生命周期章节中进行讨论。 |
destruction | 当包含该 bean 的容器被销毁时,使用回调方法。它将会在 bean 的生命周期章节中进行讨论。 |
scope:声明bean的作用域。
作用域 | 描述 |
---|---|
singleton | 在每个Spring IoC容器中一个bean定义对应一个对象实例。 |
prototype | 一个bean定义对应多个对象实例。 |
request | 在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
session | 在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
global-session | 在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。 |
2)bean的生命周期
- 初始化回调:实例化bean时,立即调用该方法。
<bean id="userService" class="com.service.UserService" init-method="init"/>
public class UserService{
public void init(){
}
}
- 销毁回调:从容器中移除bean后,才能调用该方法。
<bean id="userService" class="com.service.UserService" destroy-method="destroy"/>
public class UserService{
public void destroy(){
}
}
2、关于IOC
1)IOC
IOC控制反转:把创建对象(bean),和维护对象的关系的权利从程序中转移到spring的容器。一般的java程序我们如果需要使用某个类,我们需要定义该对象的引用,然后通过new的方法进行实例化,然后去使用该对象,但是有了spring的IOC后,我们只需定义对象的引用,然后由spring进行管理,实例化该对象供我们使用,降低了类与类之间的耦合。
2)DI
DI依赖注入:当一个bean对象引用了另外一个bean对象时,spring容器会帮我们创建依赖的bean对象,并注入到另一个bean对象中。有三种注入方式,setter方式注入,构造方法注入,注解注入。
3、依赖注入
1)属性注入
a、setter方法注入
<bean id="userService" class="com.service.UserService">
<property name="name">
<value>文学</value>
</property>
</bean>
b、构造方法注入
<bean id="userService" class="com.service.UserService">
<constructor-arg index="0" value="wenxue"/>
</bean>
c、注解注入
public class TestService{
@Autowired
private UserService userService;//不需要get、set方法
}
<!--启用注解配置,自动注入所需对象-->
<context:annotation-config/>
<bean id="userService" class="com.service.UserService">
<property name="name">
<value>文学</value>
</property>
</bean>
2)集合装配
a、Set
<bean id="userService" class="com.service.UserService">
<property name="sets">
<set>
<value>Set1</value>
<value>Set2</value>
<value>Set3</value>
</set>
</property>
</bean>
public class UserService{
private Set<String> sets=new HashSet<String>();
public Set<String> getSets(){
return sets;
}
public void setSets(Set<String> sets){
this.sets=sets;
}
}
b、List
<bean id="userService" class="com.service.UserService">
<property name="lists">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
</bean>
public class UserService{
private List<String> lists=new ArrayList<String>();
public List<String> getLists(){
return lists;
}
public void setLists(List<String> lists){
this.lists = lists;
}
}
c、Map
<bean id="userService" class="com.service.UserService">
<property name="maps">
<map>
<entry key="key1" value="Map1"></entry>
<entry key="key2" value="Map2"></entry>
<entry key="key3" value="Map3"></entry>
</map>
</property>
</bean>
public class UserService{
private Map<String, String> maps=new HashMap<String, String>();
public Map<String, String> getmaps(){
return maps;
}
public void setMaps(Map<String, String> maps){
this.maps = maps;
}
}
d、prop
<bean id="userService" class="com.service.UserService">
<property name="properties">
<props>
<prop key="key1">Properties1</prop>
<prop key="key2">Properties2</prop>
<prop key="key3">Properties3</prop>
</props>
</property>
</bean>
public class UserService{
private Properties properties=new Properties();
public Properties getProperties(){
return properties;
}
public void setProperties(Properties properties){
this.properties = properties;
}
}
3)自动扫描
大的项目中,通常会有上百个组件,如果这些组件采用xml的bean定义来配置,显然会使配置文件显得很臃肿,查找和维护起来不方便。所以引入了组件自动扫描机制,它可以在类路径下寻找标记了@Component
、@Service
、@Controller
、@Repository
注解的类,并把这些类纳入到spring容器中管理,它的作用和在xml中使用bean节点配置组件一样。要使用自动扫描机制,我们需要把配置文件如下配置:
<context:component-scan base-package="com.wenxue.service"/>
@Component
public class UserService{
}
所有的 @Repository
, @Service
或 @Controller
被注解为 @Component
,可以只使用 @Component
对所有组件进行自动扫描。它工作正常,但不是一个好的做法,为便于阅读,应该始终声明@Repository
,@ Service
或 @Controller
在指定的层,使代码更易于阅读。
4、获取Bean工厂
ApplicationContext继承自BeanFactory接口,用来对Bean进行实例化与依赖关系的装配。
1)通过类路径
ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
2)通过文件路径
ApplicationContext ac=new FileSystemXmlApplicationContext("src/com/wenxue/beans.xml");
3)通过web应用
WebApplicationContext wac=WebApplicationContextUtils.getWebApplicationContext(ServletContext sc);
三、AOP
1、介绍
1)定义
AOP(aspect oriented programming)面向切面编程:AOP是对OOP的补充和完善,将那些与业务模块无关却被业务模块所共同调用的代码封装起来,集中进行处理,然后利用一种横切的技术应用到目标对象,实现在不增加目标对象代码的前提下,还增加相应功能,减少了系统重复的代码,降低了系统的耦合度,并有利于未来的可维护性和可扩展性。
2)术语
- 通知:定义了切面的功能,以及何时应用。
- 连接点:程序应用通知的时机,可以是方法调用时、异常抛出时。
- 切入点:定义了通知应该应用在哪些连接点,通知将在这些位置执行。
- 切面:通知和切入点共同组成了切面。
- 引入:为已存在的类添加新方法和属性。
- 代理:将通知应用到目标对象后创建的对象。
- 织入:将切面应用到目标对象从而创建一个新代理对象的过程。
3)通知
- Around:拦截对目标方法调用
org.aopalliance.intercept.MethodInterceptor
- Before:在目标方法调用前调用
org.springframework.aop.MethodBeforeAdvice
- After:在目标方法调用后调用
org.springframework.aop.AfterReturningAdvice
- Throws:在目标方法抛出异常时调用
org.springframework.aop.ThrowsAdvice
4)spring对aop的支持
spring中aop代理由spring的ioc容器负责生成、管理,其依赖关系也由ioc容器负责管理。
spring创建代理的规则为:
- 默认使用java作为动态代理来创建AOP代理,这样会为任何接口实例创建代理。
- 当需要代理的类不是代理接口时,spring会切换为使用CGLIB
2、应用流程
- 配置通知:实现接口org.springframework.aop.MethodBeforeAdvice
<bean id="myMethodBeforeAdvice" class="org.springframework.aop.MyMethodBeforeAdvice"/>
- 配置代理对象
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactory"/>
- 配置代理接口集
<property name="proxyInterfaces">
<list>
<value>com.wenxue.service.TestService</value>
</list>
</property>
- 把通知织入到代理对象
<property name="interceptorNames">
<list>
<value>myMethodBeforeAdvice</value>
</list>
</property>
- 配置被代理对象
<property name="target" ref="testServcieImpl"/>
3、使用
AOP编程的主要目的就是在不增加目标对象原有代码的前提下,还给目标对象增加相应功能。在这里我们就可以知道,我们需要做的就是两件事,一是要编写增加的功能代码,也就是通知;二是配置要增加功能的位置,也就是切入点。
在实际的开发过程中一般有两种AOP的使用,在面向切面编程时,我们使用<aop:aspect/>
方式;在进行事务管理时,我们使用<aop:advisor/>
方式
1)aspect方式
<aop:aspect/>
方式是用来定义切面,是横切逻辑,就是要在连接点上做些什么,可以多次使用,具有可重用性,而且可以有多个切入点和通知,大多用于日志、缓存。这种方式对于通知,我们只需要普通的java bean对象即可。
要增加的功能部分,通知:
public class TimeHandler {
public void before(){
System.out.println("方法前:"+System.currentTimeMillis());
}
public void after(){
System.out.println("方法后:"+System.currentTimeMillis());
}
}
配置增加功能的位置,切入点:
<!-- 要增加的功能,通知 -->
<bean id="timeHandler" class="com.wenxue.study.controller.TimeHandler"/>
<aop:config>
<!-- 配置切面 -->
<aop:aspect id="time" ref="timeHandler">
<aop:pointcut expression="execution(* com.wenxue.study.service.Hello.*(..))" id="helloMethod"/>
<aop:before method="before" pointcut-ref="helloMethod"/>
<aop:after method="after" pointcut-ref="helloMethod"/>
</aop:aspect>
</aop:config>
从这里我们也可以看出切面正是由切入点和通知组成,<aop:pointcut/>
中配置了切入点,在这里是指出了要应用通知的位置是在Hello
类中的所有方法里。<aop:before/>
和<aop:after/>
则是将切入点和通知联系起来,它表明在我们之前配置的切入点中应用通知,<aop:before/>
是表示在目标对象的方法调用前执行通知中的before()
方法,也就是我们常说的前置通知;<aop:after/>
是表示在目标方法调用后执行通知中的after()
方法,也就是我们常说的后置通知。
测试:
public static void main(String[] args) {
@SuppressWarnings("resource")
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
Hello hello1=(Hello) applicationContext.getBean("helloImp1");
Hello hello2=(Hello) applicationContext.getBean("helloImp2");
hello1.firstMethod();
System.out.println();
hello1.secondMethod();
System.out.println();
hello2.firstMethod();
System.out.println();
hello2.secondMethod();
}
2)advisor方式
<aop:advisor/>
方式是定义通知器,也就是在哪些切入点上应用什么<aop:aspect/>
,这样的好处是让多个横切逻辑(即<aop:aspect/>
)多次使用,提供可重用性,一般用于事务处理。这种方式对于通知,我们必须要实现Advice接口。
通知:
public class TimeAdvice implements MethodBeforeAdvice,AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法后:advice"+System.currentTimeMillis());
}
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法前:advice"+System.currentTimeMillis());
}
}
切入点:
<bean id="timeAdvice" class="com.wenxue.study.controller.TimeAdvice"/>
<aop:config>
<aop:pointcut expression="execution(* com.wenxue.study.service.Hello.*(..))" id="helloMethod"/>
<aop:advisor advice-ref="timeAdvice" pointcut-ref="helloMethod"/>
</aop:config>
这种方式我们可以更清楚的看到,切面由通知和切入点组成,然后使用<aop:advisor/>
元素进行联系,来完成我们的AOP编程,实现对目标对象增加新的功能。
4、事务处理
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/ssm
jdbc.username=root
jdbc.password=root
applicationContext-dao.xml
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 数据源,使用DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
applicationContext-transaction.xml
<!-- 事务控制,对mysql操作数据库事务控制,spring使用jdbc的事务控制类 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 传播行为 -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.wenxue.service.impl.*.*(..))"/>
</aop:config>