面向切面编程AOP
2017-02-15 15:54
197 查看
AOP全称是Aspect Oriented Programing,即面向切面编程。AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码,主要体现在事物处理、日志管理、权限控制、异常处理等方面,是开发人员在编写业务逻辑时可以专心于核心业务,提高代码的可维护性。
目前最流行的AOP框架有两个,分别为Spring AOP 和AspecJ。Spring AOP使用纯java实现,不需要专门的编译过程和类加载器。在运行期通过代理方式向目标类织入增强的代码。AspectJ是一个机遇java语言的AOP框架,从Spring2.0开始,SpringAOP引入Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
AOP的专业术语包括Joinpoint、Pointcut、Advice、Target、Weaving、Proxy、和Aspect。
Joinpoint(连接点):是指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法。
Pointcut(切入点):是指要对哪些Joinpoint进行拦截,即被拦截的连接点。
Advice(通知):是指拦截到Joinpoint之后要做的事情,即对切入点增强的内容。
Target(目标):是指代理的目标对象。
Weaving(织入):是指把增强代码应用到目标上,生成代理对象的过程。
Proxy(代理):是指生成的代理对象。
Aspect(切面):是切入点和通知的结合。
1.手动代理
AOP手动使用代理模式有两个典型的例子,分别为JDK动态代理和CGLIB代理。
JDK动态代理
JDK动态代理通过JDK中的java.lang.reflect.Proxy类来实现,以下为JDK动态代理的实例:
UserDao.java
package cn.dao
public interface UserDao {
public void save();
public void update();
public void delete();
public void find();
}
UserDaoImpl.java
package cn.dao;
public class UserDaoImpl implements UserDao {
@Override
public void save() {
// int i = 10/0;
System.out.println("save添加用户");
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("update修改用户");
}
@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("delete删除用户");
}
@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("find查询用户");
}
}
MyAspect.java
package cn.jdk;
//切面类:可以存在多个通知Advice(增强的方法内容)
public class MyAspect {
public void myBefore(){
System.out.println("方法执行前");
}
public void myAfter(){
System.out.println("方法执行后");
}
}
MyBeanFactory.java
package cn.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import javax.xml.ws.spi.Invoker;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
import cn.dao.UserDao;
import cn.dao.UserDaoImpl;
public class MyBeanFactory {
public static UserDao getBean() {
// 1.准备目标类(spring创建对象,IOC)
final UserDao userDao = new UserDaoImpl();
// 2.创建切面类实例
final MyAspect myAspect = new MyAspect();
// 3.使用代理类,进行增强,参数2:userDao.getClass().gentInterfaces()
return (UserDao) Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
new Class[] { UserDao.class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 前增强
myAspect.myBefore();
// 目标类的方式
Object obj = method.invoke(userDao, args);
// 后增强
myAspect.myAfter();
return obj;
}
});
}
}
TestJDK.java
package cn.jdk;
import org.junit.Test;
import cn.dao.UserDao;
public class TestJDK {
@Test
public void demo01(){
//1.从工厂获得指定的内容(相当于spring获得,但此内容,是代理对象)
UserDao userDao = MyBeanFactory.getBean();
//2.执行方法
userDao.save();
userDao.update();
userDao.delete();
userDao.find();
}
}
控制台输出结果:
方法执行前
save添加用户
方法执行后
方法执行前
update修改用户
方法执行后
方法执行前
delete删除用户
方法执行后
方法执行前
find查询用户
方法执行后
CGLIB代理
JDK动态代理用起来非常简单,但它有局限性的,使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,那么可以使用CGLIB代理。
package cn.dao;
//target目标类
public class BookDao {
public void save(){
System.out.println("save添加图书");
}
public void update(){
System.out.println("update修改图书");
}
public void delete(){
System.out.println("delete删除图书");
}
public void find(){
System.out.println("find查询图书");
}
}
MyBeanFactory.java
package cn.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodProxy;
import cn.dao.BookDao;
import cn.jdk.MyAspect;
public class MyBeanFactory {
public static BookDao getBean(){
//1.准备目标类(spring创建目标,IOC)
final BookDao bookDao = new BookDao();
//2.创建切面类实例
final MyAspect myAspect = new MyAspect();
//3.生成代理类,CGLIB在运行时,生成指定对象的子类,增强
//3.1核心类
Enhancer enhancer = new Enhancer();
//3.2确定需要增强的类
enhancer.setSuperclass(bookDao.getClass());
//3.3添加回调函数
enhancer.setCallback(new MethodInterceptor(){
//intercept相当于jdk invoke,前三个参数与jdk invoke一致
@Override
public Object intercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
//#1之前
myAspect.myBefore();
//#2目标方法执行
Object obj = method.invoke(bookDao, args);
//#3之后
myAspect.myAfter();
return obj;
}
});
//3.4创建代理类
BookDao bookDaoProxy = (BookDao) enhancer.create();
return bookDaoProxy;
}
}
TestCGLIB.java
package cn.cglib;
import org.junit.Test;
import cn.dao.BookDao;
public class TestCGLIB {
@Test
public void demo01(){
//1.从工厂获得指定的内容(相当于spring获得,但此内容,是代理对象)
BookDao bookDao = MyBeanFactory.getBean();
//2.执行方法
bookDao.save();
bookDao.update();
bookDao.delete();
bookDao.find();
}
}
控制台输出结果:
方法执行前
save添加图书
方法执行后
方法执行前
update修改图书
方法执行后
方法执行前
delete删除图书
方法执行后
方法执行前
find查询图书
方法执行后
2.声明式工厂Bean
Spring通知类型:前置通知,后置通知,环绕通知(方法执行前后),异常抛出通知,引介通知(在目标类中添加一些新的方法和属性,可以应用于修改老版本程序)。
环绕通知:MethodInterceptor
MyAspect.java
package
cn.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//1.需要实现接口,确定哪个通知,及高速spring应该执行哪个方法
public class MyAspect implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("方法执行之前");
//2.执行目标方法
Object obj = mi.proceed();
System.out.println("方法执行之后");
return obj;
}
}
applicationContext.xml
<?xml
version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1.目标类-->
<bean id="userDao" class="cn.dao.UserDaoImpl"></bean>
<!-- 2.通知advice -->
<bean id="myAspect" class="cn.factorybean.MyAspect"></bean>
<!-- 3.生成代理对象 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.1代理实现的接口 -->
<property name="interfaces" value="cn.dao.UserDao"></property>
<!-- 3.2目标 -->
<property name="target" ref="userDao"></property>
<!-- 3.3用通知增强目标 -->
<property name="interceptorNames" value="myAspect"></property>
<!-- 3.4如何生成代理,true:使用cglib,false:使用jdk动态代理 -->
<property name="proxyTargetClass" value="true"></property>
</bean>
</beans>
TestFactoryBean.java
package
cn.factorybean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.dao.UserDao;
public class TestFactoryBean {
@Test
public void demo01(){
String xmlPath="cn/factorybean/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//1.从spring容器中获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
//2.指定方法
userDao.save();
userDao.update();
userDao.delete();
userDao.find();
}
}
控制台输出结果:
方法执行前
save添加用户
方法执行后
方法执行前
update修改用户
方法执行后
方法执行前
delete删除用户
方法执行后
方法执行前
find查询用户
方法执行后
3.AspectJ开发
基于XML的声明式AspectJ
基于XML的声明式AspectJ是指,通过XML文件中进行配置,来定义切面、切入点及声明通知,所有的切面和通知都必须定义在<aop:config>元素内。
MyAspect.java
package cn.aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 切面类,在此编写通知
* 可以在XML配置文件中确定通知类型
*/
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint){
System.out.print("前置通知,目标:");
System.out.print(joinPoint.getTarget()+",方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
//后置通知
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("后置通知,方法名称:"+joinPoint.getSignature().getName());
}
//环绕通知
//ProceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法
//*1.必须返回Object类型值
//*2.必须接受一个参数,类型为ProceedingJoinPoint
//*3.必须throws Throwable
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
//开始
System.out.println("环绕开始");
//执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
//结束
System.out.println("环绕结束");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知"+"出错了"+e.getMessage());
}
//最终通知
public void myAfter(){
System.out.println("最终通知");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="cn.dao.UserDaoImpl"></bean>
<!-- 2 切面 -->
<bean id="myAspect" class="cn.aspectj.xml.MyAspect"></bean>
<!-- 3 aop编程 -->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* cn.itcast.dao..*.*(..))"
id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- #1前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- #2后置通知,在方法返回之后执行,就可以获得返回值 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!-- #3环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- #4 抛出通知:用于处理程序发生异常,就可以接收当前方法产生的异常 -->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!-- #5最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
TestXML.java
package cn.aspectj.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.dao.UserDao;
public class TestXML {
@Test
public void demo01(){
String xmlPath="cn/aspectj/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//1.从spring容器中获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
//2.指定方法
userDao.save();
}
}
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
环绕开始
最终通知
异常通知出错了/by zero
基于Annotation的声明式AspectJ
与传统的AOP编程方式相比,基于XML的声明式ApectJ便捷的多,但是它的缺点是要在Spring文件中配置大量的信息,为了解决这个问题,AspectJ框架为AOP提供了一套Annotation注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿的代码。关于Annotation注解的介绍具体如下:
@AspectJ:用于定义一个切面。
@Before:用于定义前置通知,相当于BeforeAdvice
@AfterReturning:用于定义后置通知,相当于AfterReturnAdvice
@Around:用于定义环绕通知,相当于MethodInterceptor
@AfterThrowing:用于定义抛出通知,相当于ThrowAdvice
@After:用于定义最终final通知,不管是否异常,该通知都会执行
@DeclareParents:用于定义引介通知,相当于IntroductionInterceptor
MyAspect.java
package cn.aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/*
* 切面类,在此编写通知
* 基于注解实现AOP编程
*/
@Aspect
@Component
public class MyAspect {
// 用于取代: <aop:pointcut
// expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/>
// 要求: 方法必须是private 没有值 名称自定义 ,没有参数
@Pointcut("execution(* cn.dao..*.*(..))")
private void myPointCut() {
}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 , 目标:");
System.out.print(joinPoint.getTarget() + ", 方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
// 后置通知
@AfterReturning(value = "myPointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知" + joinPoint.getSignature().getName());
}
// 环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
// 开始
System.out.println("环绕开始");
// 执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
// 结束
System.out.println("环绕结束");
return obj;
}
// 异常通知
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知:出错了" + e.getMessage());
}
// 最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包:使注解生效 -->
<context:component-scan base-package="cn"></context:component-scan>
<!-- 使切面开启自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
TestAnnotation.java
package cn.aspectj.annotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.dao.UserDao;
//@RunWith(SpringJUnit4ClassRunner.class)//表示这是一个JUnit4的测试程序
@ContextConfiguration("classpath:cn/aspectj/annotation/applicationContext.xml") //定义了Spring配置文件的路径
public class TestAnnotation {
@Autowired
//将UserDao接口的实现类对象注入到该测试类中
private UserDao userDao;
@Test
public void demo01() {
// 2 执行方法
userDao.save();
}
}
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
环绕开始
最终通知
异常通知出错了/by zero
删除save()方法中的错误代码,重新运行save()方法,控制台结果为
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
save添加用户
环绕开始
最终通知
后置通知save
目前最流行的AOP框架有两个,分别为Spring AOP 和AspecJ。Spring AOP使用纯java实现,不需要专门的编译过程和类加载器。在运行期通过代理方式向目标类织入增强的代码。AspectJ是一个机遇java语言的AOP框架,从Spring2.0开始,SpringAOP引入Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
AOP的专业术语包括Joinpoint、Pointcut、Advice、Target、Weaving、Proxy、和Aspect。
Joinpoint(连接点):是指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法。
Pointcut(切入点):是指要对哪些Joinpoint进行拦截,即被拦截的连接点。
Advice(通知):是指拦截到Joinpoint之后要做的事情,即对切入点增强的内容。
Target(目标):是指代理的目标对象。
Weaving(织入):是指把增强代码应用到目标上,生成代理对象的过程。
Proxy(代理):是指生成的代理对象。
Aspect(切面):是切入点和通知的结合。
1.手动代理
AOP手动使用代理模式有两个典型的例子,分别为JDK动态代理和CGLIB代理。
JDK动态代理
JDK动态代理通过JDK中的java.lang.reflect.Proxy类来实现,以下为JDK动态代理的实例:
UserDao.java
package cn.dao
public interface UserDao {
public void save();
public void update();
public void delete();
public void find();
}
UserDaoImpl.java
package cn.dao;
public class UserDaoImpl implements UserDao {
@Override
public void save() {
// int i = 10/0;
System.out.println("save添加用户");
}
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("update修改用户");
}
@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("delete删除用户");
}
@Override
public void find() {
// TODO Auto-generated method stub
System.out.println("find查询用户");
}
}
MyAspect.java
package cn.jdk;
//切面类:可以存在多个通知Advice(增强的方法内容)
public class MyAspect {
public void myBefore(){
System.out.println("方法执行前");
}
public void myAfter(){
System.out.println("方法执行后");
}
}
MyBeanFactory.java
package cn.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import javax.xml.ws.spi.Invoker;
import org.aspectj.apache.bcel.generic.InvokeInstruction;
import cn.dao.UserDao;
import cn.dao.UserDaoImpl;
public class MyBeanFactory {
public static UserDao getBean() {
// 1.准备目标类(spring创建对象,IOC)
final UserDao userDao = new UserDaoImpl();
// 2.创建切面类实例
final MyAspect myAspect = new MyAspect();
// 3.使用代理类,进行增强,参数2:userDao.getClass().gentInterfaces()
return (UserDao) Proxy.newProxyInstance(
MyBeanFactory.class.getClassLoader(),
new Class[] { UserDao.class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// 前增强
myAspect.myBefore();
// 目标类的方式
Object obj = method.invoke(userDao, args);
// 后增强
myAspect.myAfter();
return obj;
}
});
}
}
TestJDK.java
package cn.jdk;
import org.junit.Test;
import cn.dao.UserDao;
public class TestJDK {
@Test
public void demo01(){
//1.从工厂获得指定的内容(相当于spring获得,但此内容,是代理对象)
UserDao userDao = MyBeanFactory.getBean();
//2.执行方法
userDao.save();
userDao.update();
userDao.delete();
userDao.find();
}
}
控制台输出结果:
方法执行前
save添加用户
方法执行后
方法执行前
update修改用户
方法执行后
方法执行前
delete删除用户
方法执行后
方法执行前
find查询用户
方法执行后
CGLIB代理
JDK动态代理用起来非常简单,但它有局限性的,使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,那么可以使用CGLIB代理。
package cn.dao;
//target目标类
public class BookDao {
public void save(){
System.out.println("save添加图书");
}
public void update(){
System.out.println("update修改图书");
}
public void delete(){
System.out.println("delete删除图书");
}
public void find(){
System.out.println("find查询图书");
}
}
MyBeanFactory.java
package cn.cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodProxy;
import cn.dao.BookDao;
import cn.jdk.MyAspect;
public class MyBeanFactory {
public static BookDao getBean(){
//1.准备目标类(spring创建目标,IOC)
final BookDao bookDao = new BookDao();
//2.创建切面类实例
final MyAspect myAspect = new MyAspect();
//3.生成代理类,CGLIB在运行时,生成指定对象的子类,增强
//3.1核心类
Enhancer enhancer = new Enhancer();
//3.2确定需要增强的类
enhancer.setSuperclass(bookDao.getClass());
//3.3添加回调函数
enhancer.setCallback(new MethodInterceptor(){
//intercept相当于jdk invoke,前三个参数与jdk invoke一致
@Override
public Object intercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
//#1之前
myAspect.myBefore();
//#2目标方法执行
Object obj = method.invoke(bookDao, args);
//#3之后
myAspect.myAfter();
return obj;
}
});
//3.4创建代理类
BookDao bookDaoProxy = (BookDao) enhancer.create();
return bookDaoProxy;
}
}
TestCGLIB.java
package cn.cglib;
import org.junit.Test;
import cn.dao.BookDao;
public class TestCGLIB {
@Test
public void demo01(){
//1.从工厂获得指定的内容(相当于spring获得,但此内容,是代理对象)
BookDao bookDao = MyBeanFactory.getBean();
//2.执行方法
bookDao.save();
bookDao.update();
bookDao.delete();
bookDao.find();
}
}
控制台输出结果:
方法执行前
save添加图书
方法执行后
方法执行前
update修改图书
方法执行后
方法执行前
delete删除图书
方法执行后
方法执行前
find查询图书
方法执行后
2.声明式工厂Bean
Spring通知类型:前置通知,后置通知,环绕通知(方法执行前后),异常抛出通知,引介通知(在目标类中添加一些新的方法和属性,可以应用于修改老版本程序)。
环绕通知:MethodInterceptor
MyAspect.java
package
cn.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//1.需要实现接口,确定哪个通知,及高速spring应该执行哪个方法
public class MyAspect implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("方法执行之前");
//2.执行目标方法
Object obj = mi.proceed();
System.out.println("方法执行之后");
return obj;
}
}
applicationContext.xml
<?xml
version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 1.目标类-->
<bean id="userDao" class="cn.dao.UserDaoImpl"></bean>
<!-- 2.通知advice -->
<bean id="myAspect" class="cn.factorybean.MyAspect"></bean>
<!-- 3.生成代理对象 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 3.1代理实现的接口 -->
<property name="interfaces" value="cn.dao.UserDao"></property>
<!-- 3.2目标 -->
<property name="target" ref="userDao"></property>
<!-- 3.3用通知增强目标 -->
<property name="interceptorNames" value="myAspect"></property>
<!-- 3.4如何生成代理,true:使用cglib,false:使用jdk动态代理 -->
<property name="proxyTargetClass" value="true"></property>
</bean>
</beans>
TestFactoryBean.java
package
cn.factorybean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.dao.UserDao;
public class TestFactoryBean {
@Test
public void demo01(){
String xmlPath="cn/factorybean/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//1.从spring容器中获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDaoProxy");
//2.指定方法
userDao.save();
userDao.update();
userDao.delete();
userDao.find();
}
}
控制台输出结果:
方法执行前
save添加用户
方法执行后
方法执行前
update修改用户
方法执行后
方法执行前
delete删除用户
方法执行后
方法执行前
find查询用户
方法执行后
3.AspectJ开发
基于XML的声明式AspectJ
基于XML的声明式AspectJ是指,通过XML文件中进行配置,来定义切面、切入点及声明通知,所有的切面和通知都必须定义在<aop:config>元素内。
MyAspect.java
package cn.aspectj.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 切面类,在此编写通知
* 可以在XML配置文件中确定通知类型
*/
public class MyAspect {
//前置通知
public void myBefore(JoinPoint joinPoint){
System.out.print("前置通知,目标:");
System.out.print(joinPoint.getTarget()+",方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
//后置通知
public void myAfterReturning(JoinPoint joinPoint){
System.out.println("后置通知,方法名称:"+joinPoint.getSignature().getName());
}
//环绕通知
//ProceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法
//*1.必须返回Object类型值
//*2.必须接受一个参数,类型为ProceedingJoinPoint
//*3.必须throws Throwable
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
//开始
System.out.println("环绕开始");
//执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
//结束
System.out.println("环绕结束");
return obj;
}
//异常通知
public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("异常通知"+"出错了"+e.getMessage());
}
//最终通知
public void myAfter(){
System.out.println("最终通知");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1 目标类 -->
<bean id="userDao" class="cn.dao.UserDaoImpl"></bean>
<!-- 2 切面 -->
<bean id="myAspect" class="cn.aspectj.xml.MyAspect"></bean>
<!-- 3 aop编程 -->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 3.1 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution(* cn.itcast.dao..*.*(..))"
id="myPointCut" />
<!-- 3.2 关联通知Advice和切入点pointCut -->
<!-- #1前置通知 -->
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<!-- #2后置通知,在方法返回之后执行,就可以获得返回值 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!-- #3环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!-- #4 抛出通知:用于处理程序发生异常,就可以接收当前方法产生的异常 -->
<!-- * 注意:如果程序没有异常,将不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!-- #5最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
TestXML.java
package cn.aspectj.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.dao.UserDao;
public class TestXML {
@Test
public void demo01(){
String xmlPath="cn/aspectj/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//1.从spring容器中获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
//2.指定方法
userDao.save();
}
}
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
环绕开始
最终通知
异常通知出错了/by zero
基于Annotation的声明式AspectJ
与传统的AOP编程方式相比,基于XML的声明式ApectJ便捷的多,但是它的缺点是要在Spring文件中配置大量的信息,为了解决这个问题,AspectJ框架为AOP提供了一套Annotation注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿的代码。关于Annotation注解的介绍具体如下:
@AspectJ:用于定义一个切面。
@Before:用于定义前置通知,相当于BeforeAdvice
@AfterReturning:用于定义后置通知,相当于AfterReturnAdvice
@Around:用于定义环绕通知,相当于MethodInterceptor
@AfterThrowing:用于定义抛出通知,相当于ThrowAdvice
@After:用于定义最终final通知,不管是否异常,该通知都会执行
@DeclareParents:用于定义引介通知,相当于IntroductionInterceptor
MyAspect.java
package cn.aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/*
* 切面类,在此编写通知
* 基于注解实现AOP编程
*/
@Aspect
@Component
public class MyAspect {
// 用于取代: <aop:pointcut
// expression="execution(* cn.itcast.dao..*.*(..))" id="myPointCut"/>
// 要求: 方法必须是private 没有值 名称自定义 ,没有参数
@Pointcut("execution(* cn.dao..*.*(..))")
private void myPointCut() {
}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.print("前置通知 , 目标:");
System.out.print(joinPoint.getTarget() + ", 方法名称:");
System.out.println(joinPoint.getSignature().getName());
}
// 后置通知
@AfterReturning(value = "myPointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
System.out.print("后置通知" + joinPoint.getSignature().getName());
}
// 环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
// 开始
System.out.println("环绕开始");
// 执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
// 结束
System.out.println("环绕结束");
return obj;
}
// 异常通知
@AfterThrowing(value = "myPointCut()", throwing = "e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知:出错了" + e.getMessage());
}
// 最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知");
}
}
applicationContext.xml
<?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:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包:使注解生效 -->
<context:component-scan base-package="cn"></context:component-scan>
<!-- 使切面开启自动代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
TestAnnotation.java
package cn.aspectj.annotation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.itcast.dao.UserDao;
//@RunWith(SpringJUnit4ClassRunner.class)//表示这是一个JUnit4的测试程序
@ContextConfiguration("classpath:cn/aspectj/annotation/applicationContext.xml") //定义了Spring配置文件的路径
public class TestAnnotation {
@Autowired
//将UserDao接口的实现类对象注入到该测试类中
private UserDao userDao;
@Test
public void demo01() {
// 2 执行方法
userDao.save();
}
}
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
环绕开始
最终通知
异常通知出错了/by zero
删除save()方法中的错误代码,重新运行save()方法,控制台结果为
控制台输出结果:
前置通知,目标:cn.dao.UserDaoImpl@429303,方法名称:save
save添加用户
环绕开始
最终通知
后置通知save
相关文章推荐
- 中文文本的字数统计(不包括标点符号且考虑英文/数字序列)
- Spring整合Struts2的配置与测试
- 提高C#编程水平的50个要点
- C++实例---数组访问(指针)
- 【机器人学】机器人开源项目KDL源码学习:(1)下载源码并在ubuntu下运行geometry部分(旋转矩阵)
- mavn项目(springMVC) 引入静态资源(js、css)等
- 使用java以及jdbc不使用第三方库执行sql文件脚本
- springmvc + mybatis使用c3p0连接mysql数据库时连接经常自动断开问题的解决方式
- C# + Socket断线重连
- java客户端操作hdfs权限问题初探
- 多线程编程
- 在C#中实现Socket端口复用
- C# 生成订单号的几种方式
- Java四种权限修饰符(public, default, protected, private)的用法和对比
- Java8 List stream经常用到的方法集合!
- Java中Volatile详解
- PHP源码解析笔记2-生命周期和Zend引擎
- Spring系列--bean容器的实例化之构造器创建bean实例(采用dtd约束)
- Struts2接收请求参数1
- Mongo 初识