Spring(二)——条件注解(三种方式)、Spring 包扫描(java配置和xml配置)、代理设计模式(静态和动态(JDK和CGLIB))、AOP(概念、开发术语、五种通知)
一、条件注解
1、条件注解介绍
比如同一个 bean 配置了很多份,在满足某种条件时,让某一个配置生效,这时就需要条件注解。
比如在公司开发时,有三种环境,开发环境,测试环境,生产环境,每个环境的信息都不一样,说到这就大概能感受到条件注解的重要性了。
2、条件注解的使用
a、方式一 ——注解@Conditional
举个例子:自动判断 windows 环境和 linux 环境,不同环境输出不同的信息:
先写一个接口,再写两个实现类:
接口:
实现类:
接着写 java 代码配置。这里要起个别名,大家的别名都相同:
这个写好以后,需要写条件判断;这里需要新开一个类,实现 Condition 接口:
linux 也一样的,改点关键字即可,这里就不放图了。然后接着就是测试:
可以看到成功输出 win。
b、方式二 ——注解@Conditional
举个例子,模拟不同环境下数据库不同的信息:
先创建实体类:
接着跟上面一样:
上图种用 dev 和 prod 对应不同的开发环境。接着看测试代码:
可以发现输出了对应的环境信息
c、方式三 —— xml文件配置
还是实体类:
接着是配置文件:
然后是测试代码:
可以发现 java 代码配置跟 xml 文件配置有一些地方都很像,理解起来很简单。
二、Spring 包扫描
可以发现上面的方式不是很方便,都有缺陷,bean 都需要一个个注册。所以 Spring 提供了包扫描快速的向 Spring 里面注册容器。
包扫描有两种方式:
java 包扫描和 xml 包扫描。
1、java 配置的包扫描
a、有参构造方式
先创建一个实体类,接着就是 三个分层:
如果把 service 注册到容器中去,其他的分层也必须注册到容器中去,因为容器里面跟外面不能相互识别。
所以这里认识不同分层对应的注解:
到目前为止,这四个注解在代码层面基本是没有区别的,就是说用哪一个其实都行,但是也只是目前为止,官方未来可能会对这四个注解继续加代码,到时候可能就各自区分开来。目前可以说是约定俗成的意义上区分开来。
那么 dao 层就这么使用:
service 层:
上图中要注意的是:如果用了有参构造方法了,就不要再写无参构造方法。可以发现:service 层的有参引入的是 dao 层。接着就是 servlet 层:
然后就是 java 配置:
最后就是测试代码:
结果是没有问题的。
b、注解方式
注解和有参方法中,官方推荐的是有参构造方法。但是实际使用中用的多的是注解。
接下来看看不用有参,用注解的方式:
官方之所以推荐有参构造方法,原因看下图:
如果是通过 new 的方式,那么 userDao 就会为 null:
这里的 userService 和容器没有任何联系,这时候再运行就会报空指针异常:
那么如果用有参构造方法的话:
就会提示你要传参数进来:
c、存在无参或者多个构造方法情况下的解决方式
如果有无参构造方法,或者有多个构造方法,可以参考下图解决:
也可以这么使用:
2、xml 配置的包扫描
其他的都大同小异:
测试代码:
实际的开发当中,更多的是使用注解的方式。
三、代理设计模式
AOP 是 Spring 中非常重要的内容,在开发中和框架使用中用到的次数也是很多。在介绍 AOP 前先来介绍代理设计模式
代理设计模式分两种,一种是静态代理,另外一种是动态代理 。
1、概念
2、静态代理设计模式
看看简单的代码:
首先是房东的:
然后是中介:
可以看到中介在房东租房的时候还多做了一些操作(功能)。然后是租客:
但是这种静态代理的问题也是很多的:
在实际使用中用的非常少。
3、动态代理设计模式
动态代理设计模式分两种:
一种是 jdk 动态代理实现,这种方式基于接口。
另一种是 CGlib 动态代理实现,这种方式基于继承。
AOP 的原理就是基于动态代理。
a、 JDK动态代理
首先先写一个接口:
接着就是实现类:
那么现在有个需求,当方法打印出来的时候,加上消耗的时间。
当然不能直接就在里面加代码,这个代理的功能其实就是在不动业务代码的同时,为特定的方法里面添加新功能。
接着就是动态代理(注意看注释):
b、CGLIB 动态代理
首先要用这玩意先添加依赖:
这种方式就不需要接口了,是通过继承的方式。但是 cglib 不仅能够代理没有接口,有接口的也能代理。
先创建一个实体类:
然后是拦截器添加功能:
然后是测试代码:
类名:
然后看结果:
可以看到第一行,带有很多 $ 符号,这个对象是动态生成的。
可以发现,这两个动态代理写下来还是比较麻烦,希望有一种方式可以简单快捷的实现动态代理的功能,那么这种方式就是 AOP 了。
四、AOP
1、概念:面向切面编程
2、AOP开发术语
作用:
3、环境依赖
4、前置通知
可以看到通知分很多种,先看看前置通知的实现:先写个接口:
然后是实现类:
然后是前置通知的代码:
当然这个拦截不会无缘无故的执行,需要配置(这里是 xml 文件配置)。拦截规则:
然后测试代码,结果:
加法效果:
减法效果:
小结:
添加新功能的代码要单独写个类,且要实现一个接口。
拦截哪个类、哪个方法,拦截以后给什么功能(或者通知),都在配置文件里面写。
**跟之前一样,最后这块生成的实现类也是 Spring 容器利用 JDK 动态代理为 ICalculator 自动生成的一个接口的实现类,不是前面自己写的 CalculatorImpl 这个实现类。 **
5、AOP的五种通知
a、AOP的前置通知
AOP 通知比前面的要简约点,先看代码:
还是先写个接口:
然后是实现类:
接着就写个拦截器,里面是准备要添加的功能(这里的方法名随便写,因为实际的通知效果实在 xml 配置文件里面写的):
然后是 xml 配置文件:
然后是测试代码:
然后结果:
b、AOP的后置通知
再来增加个后置通知:
然后 xml 配置文件:
如果想要在前置通知开始计时,后置通知结束计时,这里做不到,需要用到其他通知。
c、返回通知
返回通知不一样的地方是要接收返回值类型,所以需要多个参数接收,具体注意点看注释:
同样的, xml 配置也会有不一样的地方:
d、异常通知
xml 配置:
e、环绕通知(集前四个通知集大成者)
环绕通知是集前四个通知的集大成者:
然后是 xml 配置:
如果 pjp.proceed()方法括号里面什么都不写,传的参数就是自己写的 3+4。当然这里还可以自己自定义参数:
如果这么写就相当于传了 99 和 100 调用 add() 方法,那么结果自然是:199。
再试下计时:
结果: