Spring的两个核心概念
- IOC (Inversion of Control 控制反转)
- AOP (Aspect Orient Programming 面向切面编程)
IOC 方面用Annotation要比用XML更方便
AOP方面用XML要比用Annotation更强大
IOC
控制反转和依赖注入
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。
有一个实体操作接口和实现类
public interface UserDAO { public void save(User user); } public class UserDAOImpl implements UserDAO { public void save(User user) { System.out.println("user saved!"); } }
有一个实体类
public class User { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
有一个工具类
public class UserService { private UserDAO userDAO; public void add(User user) { userDAO.save(user); } public UserDAO getUserDAO() { return userDAO; } public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } }
由一个测试类
public class UserServiceTest { @Test public void testAdd() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService service = (UserService)ctx.getBean("userService"); User u = new User(); u.setUsername("zhangsan"); u.setPassword("zhangsan"); service.add(u); } }
注意这里红线的部分,有参数,说明可以指定xml的位置,同时他还有一个方法是xml数组,就是可以传多个xml进去
多个配置文件的好处就是团队协作,不同的人写不同的配置文件,不会冲突
从测试类本身来看,需要两个地方进行初始化
service对象的初始化,以及add方法中使用的是UserDAO 的哪一个实现类是没有指定的(也就是内部的UserDao没有初始化)。
但是加入了一个容器:
XML方式的注入
<beans > <bean id="u" class="com.bjsxt.dao.impl.UserDAOImpl"> </bean> <bean id="userService" class="com.bjsxt.service.UserService"> <property name="userDAO" ref="u" /> </bean> </beans>
- bean相当于new一个对象
- ref相当于引入一个已经new的对象
之后,在spring框架下,这个程序是可以正常运行的。
也就是说把具体对象的初始化交给这个容器来完成。这个就是控制反转的意义
控制反转意思就是说以前可以在类中控制具体的初始化,现在交给由容器完成
具体的类 从控制实现到控制抽象(接口),从实现具体的东西,到实现抽象的东西
也就是说UserService 类里面不需要初始化DAO对象
UserServiceTest 类里面也不需要初始化UserService对象
这些都交给容器来完成
service.add(u) 具体add的具体实现依赖于容器注入给的DAO对象,这个就是依赖注入
1.注入类型
主要有两种
- setter注入
- 构造方法注入
setter注入就是使用set方法
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
bean.xml不变
构造方法注入用的很少
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean>
2.name和id
<bean name="u" class="com.bjsxt.dao.impl.UserDAOImpl"> </bean>
这两个基本一致,没啥区别
3.简单属性的注入
对类中一般属性进行注入,很少用
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
4.集合注入
5.bean的生存范围
<bean name="u" class="com.bjsxt.dao.impl.UserDAOImpl" scope="XX"> </bean>
默认是singleton,单例
结果是true
如果类是struct的Action,范围是prototype
6.bean生命周期
-
Lazy-initialized beans
不加的话在用到xml
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
这句话的时候就初始化所有的bean
加上的话就是在使用getBean方法的时候初始化
相当的用的少
只有当程序启动很慢的时候,才用到
- init-method destroy-methd
public class UserService { private UserDAO userDAO; public void init() { System.out.println("init"); } public void add(User user) { userDAO.save(user); } public UserDAO getUserDAO() { return userDAO; } public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } public UserService(UserDAO userDAO) { super(); this.userDAO = userDAO; } public void destroy() { System.out.println("destroy"); } }
<bean id="userService" class="com.bjsxt.service.UserService" init-method="init" destroy-method="destroy" scope="prototype"> <constructor-arg> <ref bean="u"/> </constructor-arg> </bean>
init-method destroy-methd 不要和prototype一起用
也是很少用,比如数据源连接池的时候用destroy,关闭连接池
7.自动装配 Autowire
<bean name="userDAO" class="com.bjsxt.dao.impl.UserDAOImpl"> <property name="daoId" value="1"></property> </bean> <bean name="userDAO2" class="com.bjsxt.dao.impl.UserDAOImpl"> <property name="daoId" value="2"></property> </bean> <bean id="userService" class="com.bjsxt.service.UserService" scope="prototype" autowire="byName"> </bean>
byName自动装配/
最后一个bean没有指定,但是他所在的类里的属性是userDAO,它就根据这个name找到了第一个bean
前面都是xml形式的,下面说一下
Annotation的IOC
Annotation的注入
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/>
<bean id="u" class="com.bjsxt.dao.impl.UserDAOImpl"> </bean> <bean id="userService" class="com.bjsxt.service.UserService"> </bean> </beans>
</beans>
使用AutoWire加入注入
autowire会自动到配置文件中去找一个与参数对应类型的bean,注入进来
AutoWire可以加在任意方法上,只有这个方法需要注入
public class UserService { private UserDAO userDAO; public void add(User user) { userDAO.save(user); } @Autowire public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } }
public class UserServiceTest { @Test public void testAdd() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService service = (UserService)ctx.getBean("userService"); service.add(new User()); } }
可以看出它注入的方式是byType
但是如果
<bean id="u1" class="com.bjsxt.dao.impl.UserDAOImpl"> </bean> <bean id="u2" class="com.bjsxt.dao.impl.UserDAOImpl"> </bean> <bean id="userService" class="com.bjsxt.service.UserService"> </bean>
这时候就需要Qualifier
public class UserService { private UserDAO userDAO; public void add(User user) { userDAO.save(user); } @Autowire public void setUserDAO(@Qualifier("u1") UserDAO userDAO) { this.userDAO = userDAO; } }
autowire用的不多,下面这种用的比较多
Resouce
它默认的注入方式也是byName,如果name找不到,就byType
想指定名字,直接写
@Resource(name="u")
@Resource public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; }
以上所有的方式在配置文件里面都要写待注入的bean
使用
Component
只需要一句,其他的都不用写了
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.example"/>
org.example表示扫描这个包下面的所有类,发现写有Component的类,然后把他放在容器里面,形成一个bean。name就是类名首字母小写,如果想指定名字,就在类上加上@componet("newname"),value就是对象。推荐写名字
</bean> </beans>
@Test public void testAdd() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); UserService service = (UserService)ctx.getBean("userService"); service.add(new User()); }
那么它是怎么注入的呢
在注入类上加入
@Component public class UserDAOImpl implements UserDAO { public void save(User user) { System.out.println("user saved!"); } }
componet 相当于是一个组件,就是把这个类产生的对象当成一个组件,这个组件对于另外一个类来说就是一个资源
public class UserService { private UserDAO userDAO; public void add(User user) { userDAO.save(user); } @Resouce public void setUserDAO( UserDAO userDAO) { this.userDAO = userDAO; } }
默认得,spring中 @Component
, @Repository
, @Service
, @Controller
, 这四个注解用法一样,都可以把类当初一个组件资源
注解里面还有有Scope,postConstruct,PreDestroy(相当于xml中的init和destory)
AOP
是对面向对象的思维方式的有力补充
和面向对象并不矛盾,简单来说一个是横着编程,一个是竖着编程
也是两种实现配置方式
- Annotation
- XML
Annotation方式:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bjsxt"/> <aop:aspectj-autoproxy /> </beans>
<aop:aspectj-autoproxy /> 的意思是自动产生代理
Aspectj是一个专门产生动态代理,专门面向AOP编程的框架,sprig使用了它
切入点语法:
@Aspect @Component public class LogInterceptor { @Before("execution(public void com.bjsxt.service.UserDAO.add(com.bjsxt.User))")
public void myMethod(){};
}
@Component初始化这个类
@Aspect表示这个是个切面逻辑
@Pointcut("execution(public * com.bjsxt.service..*.add(..))") public void myMethod(){}; }
pointcut连接点集合
要加入切面的类必须要由spring管理起来,就是在bean.xml中能找到这个类,也可以说在类上加@Component
XML方式
这种方式更重要
加入使用别人的切面类,不可能往人家源码上加Annotation
切面类
public class LogInterceptor { public void myMethod(){}; public void before() { System.out.println("method before"); } public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable { System.out.println("method around start"); pjp.proceed(); System.out.println("method around end"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" .......>
<context:annotation-config /> <context:component-scan base-package="com.bjsxt"/> <bean id="logInterceptor" class="com.bjsxt.aop.LogInterceptor"></bean> <aop:config> <aop:aspect id="logAspect" ref="logInterceptor"> <aop:before method="before" pointcut="execution(public * com.bjsxt.service..*.add(..))" /> </aop:aspect> </aop:config> </beans>