Spring AOP AspectJ注解和XML配置两种实现(Maven构建)

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()------");    
	}
	
	
}

最后Spring的配置文件(名字随意定,我这里文件名是applicationContext.xml, 顺便插一句废话,struts里面的配置文件必须叫struts.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="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);
        
        
        
        
    }
}


ps:我经常在最后测试时,报错,提示找不到spring配置文件,这一般都是没有加入类路径,就是build path里没有加进去,直接加进去即可。最好在src下新建一个xml文件夹,将appplicationContext.xml放进去,然后config build path,再将xml文件夹加入Source。OK! 一切就绪,到了见证奇迹的时刻。







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);
	}
}

打印日志类LogAdvice实现了M ethodBeforeAdvice的before方法,这是每次方法执行前要插入的方法即插入自定义的日志信息,代码如下:

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"));
        
    }
}
结果:

Spring AOP AspectJ注解和XML配置两种实现(Maven构建)_第1张图片

在每个方法运行之前,都打印了自定义的日志信息。


前面插入的切片,是针对所有方法的,如何针对特定方法加入切片呢?其实就是拦截特定方法,现在我们添加一个拦截类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>

再次运行App类,密码奇迹般的被拦截,显示为***,如图:


只拦截getPassword方法,并更改密码。




你可能感兴趣的:(java,spring,AOP,maven)