一、Spring中的AOP
1.1AOP的概述:
什么是AOP:
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
AOP的优点:
性能监视、事务管理、安全检查、缓存
AOP的开发相关术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.
spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
**
1.2手动实现AOP
**
JDK的动态代理(针对实现了接口的对象产生代理)
publicclass MyJdbProxy implements InvocationHandler{
private UserService userService;
public MyJdbProxy(UserService userService){
this.userService = userService;
}
public UserService createProxy(){
// 生成UserSErvice的代理:
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), userService.getClass()
.getInterfaces(), this);
return userServiceProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 判断是否是save方法:
if(“save”.equals(method.getName())){
// 增强:
System.out.println(“权限校验===========”);
return method.invoke(userService, args);
}
return method.invoke(userService, args);
}
}
Cglib的动态代理(针对字节码进行增强,产生了目标类的子类对象)
publicclass MyCglibProxy implements MethodInterceptor{
private CustomerService customerService;
public MyCglibProxy(CustomerService customerService){
this.customerService = customerService;
}
public CustomerService createProxy(){
// 创建核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(customerService.getClass());
// 设置回调:
enhancer.setCallback(this);
// 创建代理:
CustomerService customerServiceProxy = (CustomerService) enhancer.create();
return customerServiceProxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] arg,
MethodProxy methodProxy) throws Throwable {
if(“delete”.equals(method.getName())){
Object obj = methodProxy.invokeSuper(proxy, arg);
System.out.println(“日志记录==========”);
return obj;
}
return methodProxy.invokeSuper(proxy, arg);
}
}
1.3Spring的传统的AOP:基于ProxyFactoryBean的方式的代理
Spring中的通知的类型:
前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性
Spring中的切面的类型:
Advisor : 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截.(不带有切入点的切面,默认增强类中所有方法)
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法.(带有切入点的切面)
IntroductionAdvisor : 代表引介切面,针对引介通知而使用切面
Spring中的通知的类型:
前置通知 org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知 org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知 org.aopalliance.intercept.MethodInterceptor
在目标方法执行前后实施增强
异常抛出通知 org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
引介通知 org.springframework.aop.IntroductionInterceptor
在目标类中添加一些新的方法和属性
Spring中的切面的类型:
Advisor : 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截.(不带有切入点的切面,默认增强类中所有方法)
PointcutAdvisor : 代表具有切点的切面,可以指定拦截目标类哪些方法.(带有切入点的切面)
IntroductionAdvisor : 代表引介切面,针对引介通知而使用切面
1.3Spring基于AspectJ的AOP开发
步骤一:创建一个WEB项目,引入开发包:
步骤二:引入spring的配置文件:
引入约束:aop的约束.
步骤三:创建包和类:
cn.zhcn.aspectj.demo1
CustomerService
CustomerServiceImpl
步骤四:将这个类配置到Spring中:
步骤五:使用AspectJ注解方式:
在Spring到配置文件中完成一个配置:
步骤六:编写一个切面类:
注解的概述:
* @Aspect:修饰一个类.代表这个类就是一个切面类.(切面是切入点和通知的组合)
* 通知的注解:
* @Before :前置通知.
* @AfterReturing :后置通知.
* @Around :环绕通知.
* @AfterThrowing :异常抛出通知.
* @After :最终通知.
* @DeclareParents :引介通知
定义切入点表达式:通知哪些类的哪些方法需要增强.
* 基于一个execution(“表达式”)
* 表达式语法: [访问修饰符] 方法返回值 方法包和类名称(参数)
* 1. public * *(..)
* 2. * cn.zhcn.service.Service.(..)
* 3. * cn.zhcn.service.CustomerService+.*(..)
* 4. * cn.zhcn.service..(..)
* 5. * cn.zhcn.service...(..)
@Aspect
publicclass MyAspectAnno {
@Before(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.save(..))”)
publicvoid before(){
System.out.println(“前置通知============”);
}
}
步骤七:将切面配置到Spring中.
步骤八:编写测试:
@Aspect
publicclass MyAspectAnno {
@Before(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.save(..))”)
publicvoid before(){
System.out.println(“前置通知============”);
}
}
1.3.1 AspectJ的通知的用法:
@Before:前置通知:
在执行目标方法之前完成一个操作,获得到切入点信息.
@AfterReturing:后置通知:
在目标方法执行之后完成一个操作,获得方法的返回值.
@AfterReturning(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.update(..))”,returning=”result”)
publicvoid afterReturing(Object result){
System.out.println(“后置通知============”+result);
}
@Around:环绕通知:
在目标方法执行的前和执行后完成一个操作,阻止目标方法执行.
@Around(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.delete(..))”)
publicObject around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println(“环绕前通知==========”);
Object obj = joinPoint.proceed();
System.out.println(“环绕后通知==========”);
return obj;
}
@AfterThrowing:异常抛出通知:
在目标方法出现异常的时候,完成一个操作.获得异常信息.
@AfterThrowing(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.find(..))”,throwing=”e”)
publicvoid afterThrowing(Throwable e){
System.out.println(“异常抛出通知=========”+e.getMessage());
}
@After:最终通知:
在目标方法任何情况下都会执行的操作.相当于finally中的代码.
@After(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.find(..))”)
publicvoid after(){
System.out.println(“最终通知===========”);
}
1.3.2 AspectJ的切入点:
定义切入点:
统一管理切入点的表达式.
@Pointcut(value=”execution(* cn.zhcn.aspectj.demo1.CustomerService+.find(..))”)
privatevoidmyPointcut1(){}
1.3.3 Aspect和Advisor的区别:
Advisor :传统的切面.传统切面一般都是由一个切入点和一个通知的组合.
Aspect :真正意义上的切面.由多个切入点和多个通知的组合.
1.4Spring基于XML开发
步骤一:创建一个WEB项目,引入开发包:
步骤二:引入spring的配置文件:
步骤三:创建包和类:
* cn.zhcn.aspectj.demo2
* OrderService
步骤四:配置这个类到Spring
步骤五:编写一个切面:
publicclass MyAspectXml {
publicvoid before(){
System.out.println(“前置通知===========”);
}
}
步骤六:在Spring中配置切面:
步骤七:在Spring中配置AOP:
步骤八:编写测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“classpath:applicationContext2.xml”)
publicclass SpringDemo2 {
@Resource(name=”orderService”)
private OrderService orderService;
@Test
publicvoiddemo1(){
orderService.save();
orderService.update();
orderService.delete();
orderService.find();
}
}
1.4.1 XML中配置其他的通知的类型:
后置通知:
环绕通知:
异常抛出通知:
最终通知:
常见问题:
每次执行WEB层代码,都会创建一个Spring的工厂.如何在WEB项目中只初始化一个工厂?
* 在Servlet的init方法中创建工厂?不可行,一个Servlet有一个init方法.创建一次工厂.也会资源浪费.
* 将工厂的创建,放到ServletContext对象中.每次获取的时候,从ServletContext中获取!!!
* 使用ServletContextListener
*
在web.xml配置文件中加入Spring的核心监听器: