1. 程式人生 > >[Spring]AOP切面程式設計/原理/基於註解/基於xml

[Spring]AOP切面程式設計/原理/基於註解/基於xml

AOP概念

不講廢話,面向切面就比如說有很多個業務邏輯程式碼,如果你要修改程式碼,在程式碼實現前後增加一條邏輯,比如要判斷後才執行程式碼,你總不能一條條去改各個類的程式碼。

所以切面就是說執行一個方法,這個方法變為一個切入點來配置,你可以定義這個切入點,你想在執行這個方法之前增加邏輯或者在它之後增加邏輯,都可自行配置。

原理(Proxy&CGlib)

Proxy實現(JDK的AOP,不是框架裡的)

定義一個代理工廠攔截了業務bean,先判斷。

package com.yiki.service;

public interface PersonSercvice {
	public void save(String name);
	public void update(String name,Integer pid);
	public String getPname(Integer pid);
	
	
}
package com.yiki.bean;

import com.yiki.service.PersonSercvice;

public class Person implements  PersonSercvice{
	private String user = null;	
	
	public Person() {
	}
	
	public Person(String user) {
		this.user=user;
	}

	
	
	public String getUser() {
		return user;
	}

	@Override
	public void save(String name) {
System.out.println("save'");		
	}

	@Override
	public void update(String name, Integer pid) {
System.out.println("update");		
	}

	@Override
	public String getPname(Integer pid) {
		// TODO Auto-generated method stub
		return "getname";
	}

}
package com.yiki.aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.aopalliance.intercept.Invocation;

import com.yiki.bean.Person;

public class JDKProxyFactory implements InvocationHandler {
	private Object target;

	public Object createProxyInstance(Object target) {

		this.target = target;
		Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);

		return target;

	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Person bean = (Person) this.target;

		Object result = null;
		if (bean.getUser() != null) {

			result = method.invoke(target, args);
		}

		return result;
	}

}
package com.yiki.test;

import org.junit.Test;

import com.yiki.aop.JDKProxyFactory;
import com.yiki.bean.Person;
import com.yiki.service.PersonSercvice;

public class aoptest {

	@Test
	public void testProxy(){
		JDKProxyFactory proxy = new JDKProxyFactory();
		PersonSercvice service	 = (PersonSercvice) proxy.createProxyInstance(new Person("yiki"));
		service.save("888");
		
		//PersonSercvice service	 = (PersonSercvice) proxy.createProxyInstance(new Person());沒有輸出
	}
	
}

Spring cglib實現

package com.yiki.aop;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.yiki.bean.People;
import com.yiki.bean.Person;

public class cglibFactory implements MethodInterceptor {
	private Object target;

	public Object createProxyInstance(Object target) {
		
		this.target=target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		enhancer.setCallback(this);
		
		
		return enhancer.create();
		
	}

	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy methodproxy) throws Throwable {
		People bean = (People) this.target;

		Object result = null;
		if (bean.getUser() != null) {

			result = methodproxy.invoke(target, args);
		}

		return result;

	}
}
package com.yiki.bean;

public class People {//不需要實現介面
	private String user;
	
	public People() {
	}

	public People(String user) {
		this.user = user;
	}

	public String getUser() {
		return user;
	}

	public void save(String name) {
		System.out.println("save'");
	}

}
@Test
	public void testcglib(){
		cglibFactory cglib = new cglibFactory();
		People people = (People) cglib.createProxyInstance(new People("yiki"));//為空則不輸出
		people.save("000");
		
	}

框架實現

Spring AOP註解實現(真正的Spring框架實現)

這裡只涉及最普通的註解,具體切面的匹配(就是@PointCut(匹配規則))以及一些需要傳入引數(&&args)的情況要另算……(太多了不知道怎麼寫- -。)

首先這裡要先把配置檔案自動掃描給加上。

<?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:context="http://www.springframework.org/schema/context"
	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/context
        http://www.springframework.org/schema/context/spring-context-4.2.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

	<context:annotation-config />

<!-- 自動掃描所有的bean -->
	<context:component-scan base-package="com.yiki.bean" />
	
<!-- 宣告自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面 -->
	<aop:aspectj-autoproxy />

</beans>

這裡我已經不會在配置檔案寫bean了,而是用@Compenent

package com.yiki.bean;

import org.springframework.stereotype.Component;

@Component
public class Student {
	
	public void student(){
		System.out.println("student");
	}
	
	

}

切面類

package com.yiki.bean;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
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;

@Aspect
@Component
public class aspect {
    @Pointcut("execution (* com.yiki.bean.Student.*(..))")//匹配Student下的所有方法
	private void anyMethod() {
	}// 空方法,是一個切入點

	@Before("anyMethod()")
	public void before() {
		System.out.println("before前置通知");
	}

	@AfterReturning("anyMethod()")
	public void after() {
		System.out.println("afterReturning後置通知");
	}
	
	@After("anyMethod()")
	public void doAfter(){
		System.out.println("after最終通知");
		
	}
	@Around("anyMethod()")//適合許可權控制,萬能~~~~~
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		
		Object result = pjp.proceed();
		try {
			//=AfterReturning
		} catch (Exception e) {
			// =before
		}finally {
			//後置通知
		}
		
		System.out.println("around環繞通知");
		return result;//最終
	}
		

}

測試

package com.yiki.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.yiki.bean.Student;

public class tests {
	
	public static void main(String[] args) {
		String xmlPath = "ApplicationContext.xml";
		ApplicationContext cfg = new ClassPathXmlApplicationContext(xmlPath);
		Student stu = (Student) cfg.getBean("student");
		stu.student();
		
		
	}

}

基於xml配置檔案的AOP(只支援單例模式)

遵循以下原則:

        <bean id="student" class="com.yiki.bean.Student"></bean>
	<bean id="aspect" class="com.yiki.bean.aspect"></bean>

	<!-- 把student作為一個切面宣告,這個切面的名字是aspectAOP 
	【expression】裡寫需要匹配的方法,*後面有空格
	-->
	<aop:config>
		<aop:aspect id="aspectAOP" ref="aspect">
			<aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))"  />
		</aop:aspect>
	</aop:config>

對於切入點PointCut:execution(這裡自己查官方吧,很多匹配規則)

<?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-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

	<!-- bean definition & AOP specific configuration -->


	<bean id="student" class="com.yiki.bean.Student"></bean>
	<bean id="aspect" class="com.yiki.bean.aspect"></bean>

	<!-- 把aspect作為一個切面宣告,這個切面的名字是aspectAOP 
	切入點是student裡的所有方法,==》【expression】裡寫需要匹配的方法,*後面有空格
	-->
	<aop:config>
		<aop:aspect id="aspectAOP" ref="aspect">
			<aop:pointcut id="doOne" expression="execution(* com.yiki.bean.Student.*(..))"  />
		
		<!-- advice通知配置 -->
		<aop:before method="before" pointcut-ref="doOne"/>
		<aop:after-returning method="after" pointcut-ref="doOne"/>
		<aop:around method="around" pointcut-ref="doOne"/>
		
		
		</aop:aspect>
	</aop:config>

</beans>
package com.yiki.bean;

import org.aspectj.lang.ProceedingJoinPoint;

public class aspect {
	
	public void show(){
		
		System.out.println("aspect");
		
	}
	
	public void before() {
		System.out.println("before前置通知");
	}

	public void after() {
		System.out.println("afterReturning後置通知");
	}
	
	public void doAfter(){
		System.out.println("after最終通知");
		
	}
	public Object around(ProceedingJoinPoint pjp) throws Throwable{
		
		Object result = pjp.proceed();
		try {
			//=AfterReturning
			System.out.println("之後Runing環繞通知");
		} catch (Exception e) {
			// =before
			System.out.println("之前環繞通知");
		}finally {
			//後置通知
			System.out.println("之後環繞通知");
		}
		
		System.out.println("around環繞通知");
		return result;//最終
	}
	

}