Spring AOP 的两种实现方式分别是注解@(基于AspectJ)和XML配置,虽然方式不同,但万变不离其宗,最终都是运用java反射和动态代理技术(这是JDK方式)或者java反射和CGlib(CGlib方式)。这是Spring内部支持的两种方式。
jdk方式:运用了动态代理,因此必须有接口实现。
CGlib方式:继承类,并不关心接口,因为没有用动态代理嘛。
本文主要针对java反射和动态代理技术(这是JDK方式)讲解注解和XML配置的两种AOP的实现。
Aspect注解方式:
首先创建Maven工程:eclipse 里直接默认创建就可,创建时我的名字命名是:Group id:com.dfy,Artifact id:SpringAOP,那么我的默认包名是:com.dfy.SpringAOP ,ps:使用Maven,当然前提是你需要安装Maven,(很简单,百度即可)。
创建完成后,配置POM.xml文件,代码如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.dfy</groupId> <artifactId>SpringAOP</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>SpringAOP</name> <url>http://maven.apache.org</url> <!--我一般习惯把我所引用包的版本集中放在这里,这样比较直观--> <properties> <spring.version>4.1.3.RELEASE</spring.version> <aspectj.version>1.6.11</aspectj.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <!--测试包,自动生成--> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--不用说,肯定是spring aop包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <!--spring上下文包,在加载spring配置文件时用到--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!--使用AspectJ方式注解需要相应的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> <!--使用AspectJ方式注解需要相应的包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> </dependencies> </project>看一下工程结构吧:
首先必须有接口和接口的实现。代码如下:
CustomerManager接口:
package com.dfy.SpringAOP; public interface CustomerManager { public void addCustomer(String name,String password); public void deleteCustomer(String name); public String getCustomerById(int id); public void updateCustomer(int id,String name,String password); }接口的实现:CustomerManageImpl类
package com.dfy.SpringAOP; public class CustomerManagerImpl implements CustomerManager { public void addCustomer(String name, String password) { System.out.print("加入了客户: "+name+"密码是: "+password); } public void deleteCustomer(String name) { System.out.println("删除了客户: "+name); } public String getCustomerById(int id) { System.out.println("找到了用户"); return "dfy"; } public void updateCustomer(int id, String name, String password) { System.out.println("更改了用户基本信息"); } }
注解的逻辑类:AspectJAdvice
package com.dfy.SpringAOP; 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; @Aspect public class AspectJAdvice { /** * Pointcut * 定义Pointcut,Pointcut名称为aspectjMethod,必须无参,无返回值 * 只是一个标识,并不进行调用 */ @Pointcut("execution(* get*(..))") private void aspectJMethod(){}; @Before("aspectJMethod()") public void doBefore(JoinPoint joinPoint){ System.out.println("----dobefore()开始----"); System.out.println("执行业务逻辑前做一些工作"); System.out.println("通过jointPoint获得所需内容"); System.out.println("----dobefore()结束----"); } @Around("aspectJMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("----doAround()开始----"); System.out.println("此处可做一些类似before的工作"); //核心逻辑 Object retval=pjp.proceed(); System.out.println("此处可做一些类似after的工作"); System.out.println("----doAround()结束----"); return retval; } @After(value="aspectJMethod()") public void doAfter(JoinPoint joinPoint){ System.out.println("----doAfter()开始----"); System.out.println("执行核心逻辑之后,所做工作"); System.out.println("通过jointPoint获得所需内容"); System.out.println("----doAfter()结束----"); } @AfterReturning(value="aspectJMethod()",returning="retval") public void doReturn(JoinPoint joinPoint, String retval){ System.out.println("AfterReturning()开始"); System.out.println("Return value= "+retval); System.out.println("此处可对返回结果做一些处理"); System.out.println("----AfterReturning()结束----"); } @AfterThrowing(value="aspectJMethod()", throwing="e") public void doThrowing(JoinPoint joinPoint,Exception e){ System.out.println("-----doThrowing()开始-----"); System.out.println(" 错误信息:"+e.getMessage()); System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等"); System.out.println(" 可通过joinPoint来获取所需要的内容"); System.out.println("-----End of doThrowing()------"); } }
<?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="com.dfy.SpringAOP"/> <!-- 启用Spring对基于@AspectJ aspects的配置支持 --> <!-- 激活自动代理功能 --> <aop:aspectj-autoproxy proxy-target-class="true"/> <bean id="customerManager" class="com.dfy.SpringAOP.CustomerManagerImpl"></bean> <bean id="aspectJAdvice" class="com.dfy.SpringAOP.AspectJAdvice"></bean> </beans>
测试类APP(我没用test包里面的测试类,不习惯):
package com.dfy.SpringAOP; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Hello world! * */ public class App { public static void main( String[] args ) { System.out.println( "Hello Spring AOP!" ); BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml"); CustomerManager customerManager=(CustomerManager) factory.getBean("customerManager"); customerManager.getCustomerById(2015); } }
XML配置方式:(需要编写的类相对多些,)
同样再次新建一个Maven工程,使用Maven之后,根本停不下来,呵呵...
pom.xml文件如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>MySpring</groupId> <artifactId>SpringTest</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>SpringTest</name> <url>http://maven.apache.org</url> <properties> <commons-lang.version>2.6</commons-lang.version> <spring.version>4.1.3.RELEASE</spring.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--我这里用了spring-mvc,没用aop,context,咦?奇怪了,呵呵,其实,只引入spring-mvc,maven就会帮我们关联引入aop,context--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${commons-lang.version}</version> </dependency> </dependencies> </project>
具体代码如下:
接口:ServiceBean
package com.SpringTest; interface ServiceBean { void addUser(String userName,String password); void deleteUser(String userName); boolean findUser(String userName); String getPassword(String userName); }接口实现类:ServiceBeanImpl
package com.SpringTest; import java.util.HashMap; import java.util.Map; public class ServiceBeanImpl implements ServiceBean { private String dir; private Map map=new HashMap(); public void addUser(String userName,String password){ if(!map.containsValue(userName)) map.put(userName, password); else throw new RuntimeException("user has already exited!"); } public void deleteUser(String userName){ if(!map.containsKey(userName)) throw new RuntimeException("user isn't exited"); map.remove(userName); } public boolean findUser(String userName){ return map.containsKey(userName); } public String getPassword(String userName){ return(String) map.get(userName); } public void setDir(String dir){ this.dir=dir; System.out.println("set user to:"+dir); } }
package com.SpringTest; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class LogAdvisor implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("我要打印日志喽! [log] "+target.getClass().getName()+"."+method.getName()+"( )"); } }Spring的配置文件beans.xml如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="serviceTarget" class="com.SpringTest.ServiceBeanImpl"/> <bean id="logAdvisor" class="com.SpringTest.LogAdvisor"/> <!-- 通过配置bean 实现了动态代理 proxyInterfaces:代理接口 target:实体类 intercepterNames:所动态生成的代理类拦截器--> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.SpringTest.ServiceBean</value></property> <property name="target"><ref local="serviceTarget"/></property> <property name="interceptorNames"> <list> <value>logAdvisor</value> </list> </property> </bean> </beans>
测试类App:
package com.SpringTest; /** * 利用Spring aop的组件, 实现简单的打印日志,拦截方法以修改方法,体验spring的轻量级。 */ import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; /** * Hello world! * */ public class App { public static void main( String[] args ) { System.out.println( "Hello World!" ); XmlBeanFactory factory=new XmlBeanFactory(new ClassPathResource("beans.xml")); ServiceBean service = (ServiceBean)factory.getBean("service"); service.addUser("hehe", "111"); service.addUser("haha", "222"); service.findUser("haha"); service.deleteUser("haha"); service.addUser("heihei", "333"); System.out.println("hehe's password is "+service.getPassword("hehe")); } }结果:
在每个方法运行之前,都打印了自定义的日志信息。
前面插入的切片,是针对所有方法的,如何针对特定方法加入切片呢?其实就是拦截特定方法,现在我们添加一个拦截类PasswordAdvice, 继承了MethodInterceptor,拦截getPassword方法以更改密码,并修改为* 。代码如下:
package com.SpringTest; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class PasswordAdvisor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Object ret=invocation.proceed(); if(ret==null) return null; String password=(String)ret; StringBuffer alter=new StringBuffer(password.length()); for(int i=0;i<password.length();i++){ alter.append("*"); } return alter.toString(); } }
修改bean.xml后(红色代码是相对前面添加的内容),如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="serviceTarget" class="com.SpringTest.ServiceBeanImpl"/> <bean id="logAdvisor" class="com.SpringTest.LogAdvisor"/> <span style="color:#ff0000;"><bean id="passwordAdvisorTarget" class="com.SpringTest.PasswordAdvisor"/> </span> <span style="color:#ff0000;"><!-- advice 指出建议修改方法的类 的实现 pattern 匹配getPassword修改此方法 --> <bean id="passwordAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="passwordAdvisorTarget"/> </property> <property name="patterns"> <list> <value>.*getPassword</value> </list> </property> </bean> </span> <!-- 通过配置bean 实现了动态代理 proxyInterfaces:代理接口 target:实体类 intercepterNames:所动态生成的代理类拦截器--> <bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.SpringTest.ServiceBean</value></property> <property name="target"><ref local="serviceTarget"/></property> <property name="interceptorNames"> <list> <value>logAdvisor</value> <span style="color:#ff0000;"><value>passwordAdvisor</value></span> </list> </property> </bean> </beans>
只拦截getPassword方法,并更改密码。