(1)spring ioc
IOC(Inversion of Control):控制反转也叫依赖注入。利用了工厂模式
所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
- 谁控制谁:在传统的开发模式下,我们都是采用直接 new 一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了 IOC 容器后,则直接由 IoC 容器来控制。所以“谁控制谁”,当然是 IoC 容器控制对象。
- 控制什么:控制对象。
- 为何是反转:没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。
- 哪些方面反转了:所依赖对象的获取被反转了。
将对象交给IOC容器管理,你只需要在spring配置文件中配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
DI(Dependency Injection
):构造方法注入、stter方法注入、接口注入
构造器注入
构造器注入,顾名思义就是被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。
YoungMan(BeautifulGirl beautifulGirl){
this.beautifulGirl = beautifulGirl;
}
构造器注入方式比较直观,对象构造完毕后就可以直接使用,这就好比你出生你家里就给你指定了你媳妇。
setter 方法注入
对于 JavaBean 对象而言,我们一般都是通过 getter 和 setter 方法来访问和设置对象的属性。所以,当前对象只需要为其所依赖的对象提供相对应的 setter 方法,就可以通过该方法将相应的依赖对象设置到被注入对象中。如下:
public class YoungMan {
private BeautifulGirl beautifulGirl;
public void setBeautifulGirl(BeautifulGirl beautifulGirl) {
this.beautifulGirl = beautifulGirl;
}
}
相比于构造器注入,setter 方式注入会显得比较宽松灵活些,它可以在任何时候进行注入(当然是在使用依赖对象之前),这就好比你可以先把自己想要的妹子想好了,然后再跟婚介公司打招呼,你可以要林志玲款式的,赵丽颖款式的,甚至凤姐哪款的,随意性较强。
接口方式注入
接口方式注入显得比较霸道,因为它需要被依赖的对象实现不必要的接口,带有侵入性。一般都不推荐这种方式。
关于 IOC 理论部分,笔者不在阐述,这里推荐几篇博客阅读:
- 谈谈对Spring IOC的理解:http://www.cnblogs.com/xdp-gacl/p/4249939.html
- Spring的IOC原理[通俗解释一下]:https://blog.csdn.net/m13666368773/article/details/7802126
- spring ioc原理(看完后大家可以自己写一个spring):https://blog.csdn.net/it_man/article/details/4402245
(2)spring aop
AOP:面向切面编程。(Aspect-Oriented Programming)。利用了代理模式
AOP将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。
拦截器
代理模式:before afterReturning around afterException after这些通知方法都可以通过spring的动态代理实现。
静态代理:在容器中的对象如果实现了接口或者继承了父类则采用静态代理模式。
动态代理(jdk动态代理,ciglib动态代理)
jdk动态代理:在容器中的对象如果实现了接口
ciglib动态代理:在容器中的对象没有实现接口
- 需要引入cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-3.2.5.jar
- 目标类不能为final
- 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
首先准备接口(UserService)和实现接口的目标对象(UserServiceImpl)。
1 public interface UserService {
2
3 void save();
4
5 }
1 public class UserServiceImpl implements UserService {
2
3 public void save() {
4 System.out.println("保存用户");
5 }
6
7 }
1.静态代理
public interface ISinger { 2 void sing(); 3 } 4 5 /** 6 * 目标对象实现了某一接口 7 */ 8 public class Singer implements ISinger{ 9 public void sing(){ 10 System.out.println("唱一首歌"); 11 } 12 } 13 14 /** 15 * 代理对象和目标对象实现相同的接口 16 */ 17 public class SingerProxy implements ISinger{ 18 // 接收目标对象,以便调用sing方法 19 private ISinger target; 20 public UserDaoProxy(ISinger target){ 21 this.target=target; 22 } 23 // 对目标对象的sing方法进行功能扩展 24 public void sing() { 25 System.out.println("向观众问好"); 26 target.sing(); 27 System.out.println("谢谢大家"); 28 } 29 }
测试:
1 /** 2 * 测试类 3 */ 4 public class Test { 5 public static void main(String[] args) { 6 //目标对象 7 ISinger target = new Singer(); 8 //代理对象 9 ISinger proxy = new SingerProxy(target); 10 //执行的是代理的方法 11 proxy.sing(); 12 } 13 }
总结:其实这里做的事情无非就是,创建一个代理类SingerProxy,继承了ISinger接口并实现了其中的方法。只不过这种实现特意包含了目标对象的方法,正是这种特征使得看起来像是“扩展”了目标对象的方法。假使代理对象中只是简单地对sing方法做了另一种实现而没有包含目标对象的方法,也就不能算作代理模式了。所以这里的包含是关键。
缺点:这种实现方式很直观也很简单,但其缺点是代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。如果能在运行时动态地写出代理对象,不但减少了一大批代理类的代码,也少了不断维护的烦恼,不过运行时的效率必定受到影响。这种方式就是接下来的动态代理。
2.动态代理
1 /**
2 * 动态代理1
3 *
4 * @author shihaibin
5 * @param <T>
6 *
7 */
8 public class UserServiceProxyFactory implements InvocationHandler {
9
10 Object impl;
11
12 public <T> Object getProxy(Class<T> clz) {
13 try {
14 impl = clz.newInstance();
15 } catch (InstantiationException e) {
16 // TODO Auto-generated catch block
17 e.printStackTrace();
18 } catch (IllegalAccessException e) {
19 // TODO Auto-generated catch block
20 e.printStackTrace();
21 }
22 // 生成动态代理
23 Object usProxy = Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), this);
24 // 返回
25 return usProxy;
26 }
27
28 /**
29 * 参数:1.当前代理對象 2.当前方法 3.当前方法执行的时候的参数
30 */
31 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
32 before();
33 Object invoke = method.invoke(impl, args);
34 after();
35 return invoke;
36 }
37 //可以重写
38 public void before() {
39 System.out.println("之后");
40 }
41
42 public void after() {
43 System.out.println("之后");
44 }
45 }
测试:
1 @Test
2 public void find1() {
3 // 简单的aop
4 UserServiceProxyFactory factory = new UserServiceProxyFactory();
5 UserService userServiceProxy = (UserService) factory.getProxy(UserServiceImpl.class);
6 userServiceProxy.save();
7 System.out.println(userServiceProxy instanceof UserServiceImpl);
8 }
3.cglib代理
1 /**
2 * 动态代理2 cglib代理
3 *
4 * @author shihaibin
5 *
6 */
7 public class UserServiceProxyFactory2 implements MethodInterceptor {
8
9 public <T> Object getProxy(Class<T> clz) {
10 Enhancer en = new Enhancer();// 帮我们生成代理对象
11 en.setSuperclass(clz);// 设置对谁进行代理
12 en.setCallback(this);//回调函数
13 return en.create();// 创建代理对象;
14 }
15
16 /**
17 * prxoyobj:被代理的原始对象 method:被代理的原始方法 arg:运行期的参数 methodProxy:产生的代理方法
18 */
19 public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
20 // 打开事务
21 System.out.println("打开事务!");
22 // 调用原有方法
23 Object invokeSuper = methodProxy.invokeSuper(prxoyobj, arg);
24 // 提交事务
25 System.out.println("提交事务!");
26 return invokeSuper;
27 }
28 }
测试:
1 @Test
2 public void find2() {
3 // 重写aop前后方法
4 UserServiceProxyFactory2 factoryContext = new UserServiceProxyFactory2();
5 UserService userServiceProxy = (UserService) factoryContext.getProxy(UserServiceImpl.class);
6 userServiceProxy.save();
7 // 判断代理对象是否属于被代理对象类型
8 // 代理对象继承了被代理对象=>true
9 System.out.println(userServiceProxy instanceof UserServiceImpl);// 判断是否属于被代理对象类型
10 }