一、AOP的概述
AOP是基于反射与动态代理的实现,java有反射的中间层,让AOP的实现更加简单,而C++想实现相比会较为困难。
AOP为:(aspect oriented programming),面向切面编程。是对OOP的补充,弥补OOP的不足。
作用:在不修改源码的前提下,给项目增加新的公共功能,如:权限,日志,事务(数据库中同时成功或同时失败的操作的集合)。
二、AOP的组成要数
分别有以下几点:
- advice:通知。即新增的功能。比如写日志。
- target:目标。通知加入的目标类。
- joinpoint:切点。通知加入的目标类的具体位置。
- pointcut:切线。所有切点的集合。
- aspect:切面。切线+通知。
- weaving:动词。织入。
- introduction:在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。
三、AOP原理
基于设计模式之“代理模式”。agent/proxy。
代码实现上面分为:静态代理 和 动态代理。
代理模式的原理:代理对象和被代理对象实现同样的业务接口,由代理对象代替其完成业务行为,并增加“附加行为”。
例如:我想去动物园买票,传统方式是直接到动物园门口购票。而现在我可以找个人代替我买票,那个人就是代理人,而他也要向我索取金钱(成本+代理费。。)之类的附加行为,就是代理业务。
而现在的AOP就是那个代理人,替我们去做事的代理人。
注意:spring实现aop采用的”动态代理”的方式。动态代理意思是,去帮你做事的代理对象不是固定的,是用Spring动态创建的。
四、Spring对AOP的支持
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。
AOP通知日志的简单实例,创建一个User接口,具有登陆与注册两个方法:
<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">interface</span> User {
<span style="color:#000088 !important">void</span> login();
<span style="color:#000088 !important">void</span> register();
}</code></span>
创建一个代理对象UserImpl,实现User接口:
<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">UserImpl</span> <span style="color:#000088 !important">implements</span> <span style="color:#4f4f4f !important">User</span> {
<span style="color:#000088 !important">private</span> String role;
<span style="color:#000088 !important">public</span> String <span style="color:#009900 !important">getRole</span>() {
<span style="color:#000088 !important">return</span> role;
}
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">setRole</span>(String role) {
<span style="color:#000088 !important">this</span>.role = role;
}
<span style="color:#9b859d !important">@Override</span>
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">login</span>() {
System.out.println(<span style="color:#009900 !important">"完成登录的业务方法!"</span>);
}
<span style="color:#9b859d !important">@Override</span>
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">register</span>() {
System.out.println(<span style="color:#009900 !important">"完成注册的业务方法!"</span>);
}
}</code></span>
创建一个通知类对象MyAdvice:
<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> MyAdvice {
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">beforelog</span>()
{
System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是前置通知的日志。"</span>);
}
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">afterlog</span>()
{
System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是后置通知的日志。"</span>);
}
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">aroundlog</span>(ProceedingJoinPoint p) throws Throwable
{
System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"环绕前的日志!"</span>);
p.proceed(); <span style="color:#880000 !important"><em>//让原来的业务方法执行!</em></span>
System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"环绕后的日志!"</span>);
}
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">throwlog</span>()
{
System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是异常通知的日志"</span>);
}
}</code></span>
在applicationContext.xml文件配置头部:
<span style="color:#000000"><code><beans xmlns=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans"</span>
xmlns:xsi=<span style="color:#009900 !important">"http://www.w3.org/2001/XMLSchema-instance"</span>
xmlns:aop=<span style="color:#009900 !important">"http://www.springframework.org/schema/aop"</span>
xsi:schemaLocation=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans</span>
http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/beans/spring-beans-3.0.xsd</em></span>
http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/aop</em></span>
http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/aop/spring-aop-3.0.xsd"></em></span></code></span>
接着配置其代理对象,和通知类对象及织入关系
<span style="color:#000000"><code> <span style="color:#880000 !important"><em><!-- 创建1个业务实现类对象 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"user"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.UserImpl"</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>
<span style="color:#880000 !important"><em><!--创建1个自定义通知类对象 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"myadvice"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.MyAdvice"</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>
<span style="color:#880000 !important"><em><!-- 配置 织入关系 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:config</span>></span>
<span style="color:#880000 !important"><em><!-- 定义1条切线 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.login(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span> <span style="color:#880000 !important"><em><!--这里监听的是login方法--></em></span>
<span style="color:#880000 !important"><em><!--配置切面 ,引用自定义通知对象--></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:aspect</span> <span style="color:#4f4f4f !important">ref</span>=<span style="color:#009900 !important">"myadvice"</span>></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:before</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"beforelog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:after</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"afterlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:around</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"aroundlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:after-throwing</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"throwlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:aspect</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:config</span>></span></code></span>
最后,在TestUser测试类中进行日志测试:
<span style="color:#000000"><code class="language-java"><span style="color:#000088 !important">import</span> com.sunsharing.entity.User;
<span style="color:#000088 !important">import</span> org.springframework.beans.factory.BeanFactory;
<span style="color:#000088 !important">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">TestUser</span> {
<span style="color:#9b859d !important">@org</span>.junit.Test
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">test</span>() {
BeanFactory fac = <span style="color:#000088 !important">new</span> ClassPathXmlApplicationContext(<span style="color:#009900 !important">"applicationContext.xml"</span>);
User user = (User) fac.getBean(<span style="color:#009900 !important">"user"</span>);
user.login();
}
}</code></span>
输出结果:
其中还有个抛出异常的日志监听,在register方法抛出个自定义异常试试。
<span style="color:#000000"><code><span style="color:#9b859d !important">@Override</span>
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">register</span>() {
<span style="color:#000088 !important">throw</span> <span style="color:#000088 !important">new</span> RuntimeException(<span style="color:#009900 !important">"这是注册的异常!"</span>);
<span style="color:#880000 !important"><em>//System.out.println("完成注册的业务方法!");</em></span>
}</code></span>
然后把applicationContext中的监听方法改为rigister
<span style="color:#000000"><code><span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.register(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span></code></span>
测试类中运行后,输出的结果为:
以上是日志功能的实现,接下来再讲个权限功能的实现
五、AOP的权限功能
沿用以上的例子,加个周四无法注册的权限控制。
创建一个MyRegAdvice的自定义通知类:
<span style="color:#000000"><code><span style="color:#000088 !important">package</span> com.sunsharing.entity;
<span style="color:#000088 !important">import</span> java.util.Calendar;
<span style="color:#000088 !important">import</span> org.aspectj.lang.ProceedingJoinPoint;
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">MyRegAdvice</span>
{
<span style="color:#880000 !important"><em>//这是环绕通知</em></span>
<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">checkday</span>(ProceedingJoinPoint p) <span style="color:#000088 !important">throws</span> Throwable
{
Calendar c = Calendar.getInstance();
<span style="color:#000088 !important">int</span> day = c.get(Calendar.DAY_OF_WEEK);
<span style="color:#000088 !important">if</span>(day==<span style="color:#006666 !important">5</span>)<span style="color:#880000 !important"><em>//周日是第一天为1,因此周四是5,亲测!</em></span>
{
System.out.println(<span style="color:#009900 !important">"周四无法注册!"</span>);
<span style="color:#000088 !important">return</span>;
}
p.proceed(); <span style="color:#880000 !important"><em>//其他情况,允许往前走。</em></span>
}
}</code></span>
创建一个applicationContext_register.xml配置文件:
<span style="color:#000000"><code><span style="color:#006666 !important"><<span style="color:#4f4f4f !important">beans</span> <span style="color:#4f4f4f !important">xmlns</span>=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans"</span>
<span style="color:#4f4f4f !important">xmlns:xsi</span>=<span style="color:#009900 !important">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span style="color:#4f4f4f !important">xmlns:aop</span>=<span style="color:#009900 !important">"http://www.springframework.org/schema/aop"</span>
<span style="color:#4f4f4f !important">xsi:schemaLocation</span>=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"</span>></span>
<span style="color:#880000 !important"><em><!-- 创建1个业务实现类对象 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"user"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.UserImpl"</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>
<span style="color:#880000 !important"><em><!--创建1个自定义通知类对象 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"myadvice"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.MyRegAdvice"</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>
<span style="color:#880000 !important"><em><!-- 配置 织入关系 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:config</span>></span>
<span style="color:#880000 !important"><em><!-- 定义1条切线 --></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.reg*(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#880000 !important"><em><!--配置切面 ,引用自定义通知对象--></em></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:aspect</span> <span style="color:#4f4f4f !important">ref</span>=<span style="color:#009900 !important">"myadvice"</span>></span>
<span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:around</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"checkday"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:aspect</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:config</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">beans</span>></span></code></span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
最后在测试类进行测试,测试类完全不用改动。
输出结果为: