Spring入门(2)

1.自动装配

    set注入和构造方法注入有时配置麻烦,可以采用自动装配功能,通过修改spring配置文件中<bean>标签的autowire属性。
1.byName

    从spring环境中获取目标对象时,目标对象中的属性会根据名称在整个spring环境中寻找<bean>标签的id属性,如果有相同的,那么获取这个对象,实现关联。该方法可能匹配到错误的bean,即使用了name属性,但是两个bean类型完全不同。

<bean id="person" class="testSpring.bean.Person" autowire="byName"></bean>

2.byType
   从Spring环境中获取目标对象时,目标对象中的属性会根据类型在整个spring环境中查找<bean>标签的class属性值。如果有相同的,那么获取这个对象,实现关联。

 主要有三种方式;

(1)A、B类型相同

(2)AB为所属关系

(3)A实现了B的接口

 缺点:如果存在多个相同类型的bean对象,会出错。
       如果属性为单一类型的数据,那么查找到多个关联对象会发生错误。
       如果属性为数组或集合(泛型)类型,那么查找到多个关联对象不会发生异常。
3.constructor
 使用构造方法完成对象注入,其实也是根据构造方法的参数类型进行对象查找,相当于采用byType的方式。
4.autodetect
 自动选择:如果对象没有无参数的构造方法,那么自动选择constructor的自动装配方式进行构造注入。如果对象含有无参数的构造方法,那么自动选择byType的自动装配方式进行setter注入。
5.no
 不支持自动装配功能,需要使用ref引用其他bean,代码可读性强,不易出错
6.default
 表示默认采用上一级标签的自动装配的取值。如果存在多个配置文件的话,那么每一个配置文件的自动装配方式都是独立的,
自动装配功能和手动装配要是同时使用,那么自动装配就不起作用

2.web开发整合

     在整合web开发时,每次执行Servlet都要加载Spring配置、Spring环境,我们可以将一次加载的内容放到ServletContext.同时用ServletContextListener来监听ServletContext对象的创建和销毁。

导入包spring-web-3.2.0.RELEASE.jar

web.xml配置如下:

<listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
 <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:applicationContext.xml</param-value>

 </context-param>

上述配置将Spring容器交由web容器初始化和管理,为了获得WebApplicationContext对象,要依赖ServletContext对象。

获取方式主要有下列两种:

WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

WebApplicationContext applicationContext = (WebApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

在进行测试时有两种手段,一种是引入Junit5或者直接导入spring-test-XXX.RELEASE.jar。

3.AOP

常用的aop框架有两种,springAOP和AspectJ,前者不需要专门的编译过程以及加载类,在运行期间通过代理方式向目标

类植入增强的代码,后者通过编译器在编译时提供横向代码植入。

概念解读:AOP Aspect Oriented Programing 面向切面编程,采取横向抽取机制,取代了传统纵向继承体系重复性代码(性能监视、事务管理、安全检查、缓存),不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。

AOP中的类型

基于代理的经典Spring AOP;纯POJO切面(4.x版本需要XML配置);@AspectJ注解驱动的切面;注入式AspectJ切面(适用于Spring各版本)。

Spring AOP构建在动态代理基础之上,Spring对AOP的支持局限于方法拦截。代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。

当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。直到应用需要被代理的bean时,Spring才创建代理对象。 如果使用的是ApplicationContext的话,

在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。

AOP中的术语

可以用上图来表示AOP

1.Aspect:切面, 程序需要实现的交叉功能,通常将其抽象成类、接口, AOP编程重要的就是识别出横切面

功能,其实就是写一段公共代码,最常见的情况就是日志生成。 

2.Advice:通知,切面功能的具体实现,在某个特定切面上执行的动作,它以拦截器作为模型,维护一个以连接

点为中心的拦截器链。

Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
注  解通  知
@After通知方法会在目标方法返回或抛出异常后调用
@AfterReturning通知方法会在目标方法返回后调用
@AfterThrowing通知方法会在目标方法抛出异常后调用
@Around通知方法会将目标方法封装起来
@Before通知方法会在目标方法调用之前执行

3.Pointcut:切入点,描述横切面功能应用的限制,不是所有的流程都需要,那些可以使用的地方就是切入点

,具体来说就是切面注入到程序中的位置。

静态切入点:定义了两个静态切入点的实现类,分别是不能被实例化的抽象静态类StaticMethodMatcherPointcut以及只能对方法名进行判别的静态方法NameMatchMethodPointcut,配置示例如下:

<bean id=NameMatchMethodPointcut class=org.springframework.aop.support.NameMatchMethodPointcut>
    <property name=mappedNames>
        <list>
            <value>pos*</value>
            <value>start</value>
        </list>
    </property>
</bean>

动态切入点:定义了两个静态切入点的实现类,分别是不能被实例化的抽象静态类

StaticMethodMatcherPointcut以及只能对方法名进行判别的静态方法NameMatchMethodPointcut。

4.Joinpoint: 连接点,或指组件加入流程的时机,比如设置属性,调用方法等,spring只支持方法调用的

连接点,而其他的一些框架支持属性的连接点如:AspectJ

5.Weave: 织入,将组件应用到业务流程中的过程。

6.Proxy,代理,在实现上,Spring的AOP其实就是使用JDK的动态代理(使用接口的方式完成代理操作),也

可以使用CGLIB(使用继承的方式完成代理操作)。

7.Target,目标,业务操作的实际对象。

动态代理

(1)JDK动态代理

A.创建Dao层接口并实现

B.创建aspect包并实现切面类

C.创建代理类并进行单元测试

package cn.testSpring.dao.impl;package cn.testSpring.dao;

import org.springframework.stereotype.Repository;

@Repository(value="userDao")
public interface UserDao {

	void add();
	void delete();
	void addUser();
}
import org.springframework.stereotype.Repository;

import cn.testSpring.dao.UserDao;
@Repository(value="userDao")
public class UserDaoImpl implements UserDao {
    @Override
	public void add() {
        System.out.println("UserDao Add Method.");
    }
    @Override
	public void delete() {
        System.out.println("UserDao delete Method.");
    }
	@Override
	public void addUser() {
		 System.out.println("UserDao Add Method.");
	}
}
package cn.testSpring.aspect;
//切面类:可以存在多个通知Advice(即增强的方法)
public class MyAspect {
	public void check_Permissions(){
		System.out.println("模拟检查权限...");
	}
	public void log(){
		System.out.println("模拟记录日志...");
	}
}

package cn.testSpring.jdk;

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

import cn.testSpring.aspect.MyAspect;
import cn.testSpring.dao.UserDao;
/**
 * JDK代理类
 */
public class JdkProxy implements InvocationHandler{
	// 声明目标类接口
	private UserDao userDao;
	// 创建代理方法
	public  Object createProxy(UserDao userDao) {
		this.userDao = userDao;
		// 1.类加载器
		ClassLoader classLoader = JdkProxy.class.getClassLoader();
		// 2.被代理对象实现的所有接口
		Class[] clazz = userDao.getClass().getInterfaces();
		// 3.使用代理类,进行增强,返回的是代理后的对象
		return  Proxy.newProxyInstance(classLoader,clazz,this);
	}
	/*
	 * 所有动态代理类的方法调用,都会交由invoke()方法去处理
	 * proxy 被代理后的对象 
	 * method 将要被执行的方法信息(反射) 
	 * args 执行方法时需要的参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 声明切面
		MyAspect myAspect = new MyAspect();
		// 前增强
		myAspect.check_Permissions();
		// 在目标类上调用方法,并传入参数
		Object obj = method.invoke(userDao, args);
		// 后增强
		myAspect.log();
		return obj;
	}
}
package cn.testSpring.junitTest;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import cn.testSpring.dao.UserDao;
import cn.testSpring.dao.impl.UserDaoImpl;
import cn.testSpring.jdk.JdkProxy;

class JdkProxyTest {
	@Test
	void testCreateProxy() {
		// 创建代理对象
				JdkProxy jdkProxy = new JdkProxy();
		         // 创建目标对象
				UserDao userDao= new UserDaoImpl();
				// 从代理对象中获取增强后的目标对象
				UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
				// 执行方法
				userDao1.addUser();
				userDao1.delete();
	}
}
模拟检查权限...
UserDao Add Method.
模拟记录日志...
模拟检查权限...
UserDao delete Method.
模拟记录日志...

(2)CGLIB代理

JDK动态代理方法简单,但是使用代理过程中必须实现一个或者多个接口,而CGLIB代理却不需要。CGLIB是一

个代码生成包,他对目标类生成一个子类,并对子类进行加强,spring集成了开发所需要的包,不需要再导入。

A.实现Dao层

B.在cglib包中创建CglibProxy代理类,并实现MethodInterceptor接口,并实现接口中的Intercept方法

package cn.testSpring.dao.impl;

public class userDao {
	public void addUser() {
		System.out.println("添加用户");
	}
	public void deleteUser() {
		System.out.println("删除用户");
	}
}
package cn.testSpring.Proxy;


import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import cn.testSpring.aspect.MyAspect;
// 代理类
public class CglibProxy implements MethodInterceptor{
    // 代理方法
	public  Object createProxy(Object target) {
		// 创建一个动态类对象
		Enhancer enhancer = new Enhancer();
		// 确定需要增强的类,设置其父类
		enhancer.setSuperclass(target.getClass());
		// 添加回调函数
		enhancer.setCallback(this);
		// 返回创建的代理类
		return enhancer.create();
	}
	/**
	 * proxy CGlib根据指定父类生成的代理对象
	 * method 拦截的方法
	 * args 拦截方法的参数数组
	 * methodProxy 方法的代理对象,用于执行父类的方法 
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, 
            					 MethodProxy methodProxy) throws Throwable {
         // 创建切面类对象
		MyAspect myAspect = new MyAspect();
		// 前增强
		myAspect.check_Permissions();
		// 目标方法执行
		Object obj = methodProxy.invokeSuper(proxy, args);
		// 后增强
		myAspect.log();	
		return obj;
	}
}
package cn.testSpring.junitTest;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import cn.testSpring.Proxy.CglibProxy;
import cn.testSpring.dao.impl.userDao;
class CglibProxyTest {

	@Test
	void testCreateProxy() {
		// 创建代理对象
		CglibProxy cglibProxy = new CglibProxy();
         	// 创建目标对象
		userDao userDao = new userDao();
         // 获取增强后的目标对象
		userDao userDao1 = (userDao)cglibProxy.createProxy(userDao);
		// 执行方法
		userDao1.addUser();
		userDao1.deleteUser();
	}

}
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1 (file:/D:/MyCode/MYcode/SpringStudy/WebContent/WEB-INF/lib/spring-core-5.0.5.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
模拟检查权限...
添加用户
模拟记录日志...
模拟检查权限...
删除用户
模拟记录日志...

基于代理类的AOP实现(Spring的AOP)

spring中AOP代理默认使用JDK代理方式的实现,ProxyBeanFactory是实现AOP代理的基本方式。

  • AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice,Spring按照通知Advice在目标类方法的连接点位置,可以分为5类
  • 前置通知 org.springframework.aop.MethodBeforeAdvice

  • 在目标方法执行前实施增强

  • 后置通知 org.springframework.aop.AfterReturningAdvice 

  • 在目标方法执行后实施增强

  • 环绕通知 org.aopalliance.intercept.MethodInterceptor 

  • 在目标方法执行前后实施增强

  • 异常抛出通知 org.springframework.aop.ThrowsAdvice 

  • 在方法抛出异常后实施增强

package cn.testSpring.factorybean;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class Myaspect implements MethodInterceptor{
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		check_Permissions();
		// 执行目标方法
		Object obj = mi.proceed();
		log();
		return obj;
	}
	public void check_Permissions(){
		System.out.println("模拟检查权限...");
	}
	public void log(){
		System.out.println("模拟记录日志...");
	}
}
可以通过在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-4.3.xsd">
	<!-- 1 目标类 -->
	<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
	<!-- 2 切面类 -->
	<bean id="myAspect" class="cn.testSpring.factorybean.Myaspect" />
	<!-- 定义切点切面: -->
	<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
		<!-- 定义表达式,规定哪些方法执行拦截 -->
		<!-- . 任意字符  * 任意个 -->
		<!-- <property name="pattern" value=".*"/> -->
		<!-- <property name="pattern" value="cn\.itcast\.spring3\.demo4\.OrderDao\.add.*"/> -->
		<!-- <property name="pattern" value=".*add.*"></property> -->
		<property name="patterns" value=".*addUser.*"></property>
		<!-- 应用增强 -->
		<property name="advice" ref="myAspect"/>
	</bean>
	<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
	<bean id="userDaoProxy" 
            class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 3.2 指定目标对象 -->
		<property name="target" ref="userDao" />
		<property name="proxyTargetClass" value="true"></property>  
		<property name="interceptorNames" value="myPointcutAdvisor" />
	</bean>
</beans>
<?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-4.3.xsd">
	<!-- 1 目标类 -->
	<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
	<!-- 2 切面类 -->
	<bean id="myAspect" class="cn.testSpring.factorybean.Myaspect" />
	<!-- 3 使用Spring代理工厂定义一个名称为userDaoProxy的代理对象 -->
	<bean id="userDaoProxy" 
            class="org.springframework.aop.framework.ProxyFactoryBean">
		<!-- 3.1 指定代理实现的接口-->
		<property name="proxyInterfaces" 
                      value="cn.testSpring.dao.UserDao" />
		<!-- 3.2 指定目标对象 -->
		<property name="target" ref="userDao" />
		<!-- 3.3 指定切面,织入环绕通知 -->
		<property name="interceptorNames" value="myAspect" />
		<!-- 3.4 指定代理方式,true:使用cglib,false(默认):使用jdk动态代理 -->
		<property name="proxyTargetClass" value="true" />
	</bean>
</beans>
	@Test
	void test() {
		   ApplicationContext Context = new ClassPathXmlApplicationContext("test3.xml");
		   UserDao userDao = (UserDao) Context.getBean("userDaoProxy");
		   // 执行方法
		   userDao.addUser();
		   userDao.delete();
	}
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils$1 (file:/D:/MyCode/MYcode/SpringStudy/WebContent/WEB-INF/lib/spring-core-5.0.5.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
模拟检查权限...
UserDao Add Method.
模拟记录日志...
模拟检查权限...
UserDao delete Method.
模拟记录日志...

自动代理(摘自javaEE企业及应用开发教程【SSM】)

自动创建代理(基于后处理Bean.在Bean创建的过程中完成的增强.生成Bean就是代理.)
BeanNameAutoProxyCreator 根据Bean名称创建代理
DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理

AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ 注解进行自动代理

A.BeanNameAutoProxyCreator :按名称生成代理

<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>
<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>
<!-- 自动代理:按名称的代理 基于后处理Bean,后处理Bean不需要配置ID-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="*Dao"/>**
    <property name="interceptorNames" value="beforeAdvice"/>**
</bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest{
    @Autowired
    @Qualifier("orderDao")
    private OrderDao orderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao customerDao;    
    @Test
    public void demo1(){
        orderDao.add();
        orderDao.delete();
        customerDao.update();
    }
}

B.DefaultAdvisorAutoProxyCreator :根据切面中定义的信息生成代理
<!-- 定义目标对象 -->
<bean id="customerDao" class="cn.itcast.spring3.demo3.CustomerDaoImpl"></bean>
<bean id="orderDao" class="cn.itcast.spring3.demo4.OrderDao"></bean>
<!-- 定义增强 -->
<bean id="beforeAdvice" class="cn.itcast.spring3.demo3.MyBeforeAdvice"></bean>
<bean id="aroundAdvice" class="cn.itcast.spring3.demo4.MyAroundAdvice"></bean>
<!--定义一个带有切点的切面 -->
<bean id="myPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">**
    <property name="pattern" value=".*add.*"/>
    <property name="advice" ref="aroundAdvice"/>
</bean>
<!--自动生成代理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext3.xml")
public class SpringTest6 {
    @Autowired
    @Qualifier("orderDao")
    private OrderDao orderDao;
    @Autowired
    @Qualifier("customerDao")
    private CustomerDao customerDao;
    @Test
    public void demo1(){
        orderDao.add();
        orderDao.update();
        orderDao.delete();
        customerDao.add();
    }
}

AspectJ开发

使用AspectJ来实现AOP有两种方式:基于xml声明的方式,以及基于注解声明的AspectJ。首先导入开发需要的包,aspectjweaver-1.8.13.jar以及spring-aspects-5.0.5.RELEASE.jar、spring-aop-5.0.5.RELEASE.jar。

XML中声明切面

所有的切面、切入点、通知都必须定义在<aop:config>中。
AOP配置元素用途
<aop:advisor>定义AOP通知器
<aop:after>定义AOP后置通知(不管被通知的方法是否执行成功)
<aop:after-returning>定义AOP返回通知
<aop:after-throwing>定义AOP异常通知
<aop:around>定义AOP环绕通知
<aop:aspect>定义一个切面
<aop:aspectj-autoproxy>启用@AspectJ注解驱动的切面
<aop:before>定义一个AOP前置通知
<aop:config>顶层的AOP配置元素。大多数的<aop:*>元素必须包含在<aop:config>元素内
<aop:declare-parents>以透明的方式为被通知的对象引入额外的接口
<aop:pointcut>定义一个切点
以下列例子来进行详细说明:
<?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-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	<!-- 1 目标类; 需要加强的目标类-->
	<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
	<!-- 2 切面:通常就是你需要执行的操作,如抓日志,权限验证等操作 -->
	<bean id="myAspect" class="com.itheima.aspectj.xml.MyAspect" />
	<!-- 3 aop编程(或者说配置) -->
	<aop:config>
        <!-- 配置切面 :下述代码将myAspect转化为切面bean-->
		<aop:aspect ref="myAspect">
		  <!-- 3.1 配置切入点,当他作为<aop:config>的子元素进行配置时,表示该切入点为全局切入点;当他作为<aop:aspect>的子元素时,表示只对当前切面使用。 -->
		  <aop:pointcut expression="execution(* com.itheima.jdk.*.*(..))"
				                                      id="myPointCut" />
			<!-- 3.2 关联通知Advice和切入点pointCut -->
			<!-- 3.2.1 前置通知 -->
			<aop:before method="myBefore" pointcut-ref="myPointCut" />
			<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
			 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
			<aop:after-returning method="myAfterReturning"
				pointcut-ref="myPointCut" returning="returnVal" />
			<!-- 3.2.3 环绕通知 -->
			<aop:around method="myAround" pointcut-ref="myPointCut" />
			<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
			<!-- * 注意:如果程序没有异常,将不会执行增强 -->
			<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
			<aop:after-throwing method="myAfterThrowing"
				pointcut-ref="myPointCut" throwing="e" />
			<!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
			<aop:after method="myAfter" pointcut-ref="myPointCut" />
		</aop:aspect>
	</aop:config>
</beans>

<aop:pointcut>通常包含id(aop中很多属性实际上很少直接标出来,一般不会指定id,id只是为了指定XX的唯一标识)以及expression属性,expression是切入点表达式。expression="execution(* com.itheima.jdk.*.*(..))"

execution语法:execution(<访问修饰符>? <返回类型> <方法路径>?  <方法名>(<参数>)   <异常>?)

其中带有问号的为可选配置,其余为必配。

execution(“* cn.itcast.spring.dao.*(..)”) —只检索当前包
execution(“* cn.itcast.spring.dao..*(..)”) —检索包及当前包的子包.
execution(* cn.itcast.dao.GenericDAO+.*(..)) —检索GenericDAO及子类

基于XML文件配置

(1)编写需要增强的类

(2)定义切面

(3)配置xml文件

package cn.testSpring.xml;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 *切面类,在此类中编写通知
 */
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.print("后置通知:模拟记录日志...," );
		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("最终通知:模拟方法结束后的释放资源...");
	}
}
<?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-4.3.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	<!-- 1 目标类 -->
	<bean id="userDao" class="cn.testSpring.dao.impl.UserDaoImpl" />
	<!-- 2 切面 -->
	<bean id="myAspect" class="cn.testSpring.xml.MyAspect" />
	<!-- 3 aop编程 -->
	<aop:config>
        <!-- 配置切面 -->
		<aop:aspect ref="myAspect">
		  <!-- 3.1 配置切入点,通知最后增强哪些方法 -->
		  <aop:pointcut expression="execution(* cn.testSpring.dao.impl.*.*(..))" id="myPointCut" />
			<!-- 3.2 关联通知Advice和切入点pointCut -->
			<!-- 3.2.1 前置通知 -->
			<aop:before method="myBefore" pointcut-ref="myPointCut" />
			<!-- 3.2.2 后置通知,在方法返回之后执行,就可以获得返回值
			 returning属性:用于设置后置通知的第二个参数的名称,类型是Object -->
			<aop:after-returning method="myAfterReturning"
				pointcut-ref="myPointCut" returning="returnVal" />
			<!-- 3.2.3 环绕通知 -->
			<aop:around method="myAround" pointcut-ref="myPointCut" />
			<!-- 3.2.4 抛出通知:用于处理程序发生异常-->
			<!-- * 注意:如果程序没有异常,将不会执行增强 -->
			<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
			<aop:after-throwing method="myAfterThrowing"
				pointcut-ref="myPointCut" throwing="e" />
			<!-- 3.2.5 最终通知:无论程序发生任何事情,都将执行 -->
			<aop:after method="myAfter" pointcut-ref="myPointCut" />
		</aop:aspect>
	</aop:config>
</beans>
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test5.xml");
		// 1 从spring容器获得内容
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		// 2 执行方法
		userDao.addUser();
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
前置通知 :模拟执行权限检查...,目标类是:cn.testSpring.dao.impl.UserDaoImpl@17d88132,被织入增强处理的目标方法为:addUser
环绕开始:执行目标方法之前,模拟开启事务...
UserDao Add Method.
最终通知:模拟方法结束后的释放资源...
环绕结束:执行目标方法之后,模拟关闭事务...
后置通知:模拟记录日志...,被织入增强处理的目标方法为:addUser
下面在addUser中添加int i= 1/0;进行单元测试,结果如下:
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Exception in thread "main" java.lang.ArithmeticException: / by zero
	...........
	at cn.testSpring.xml.TestXmlAspectj.main(TestXmlAspectj.java:14)
前置通知 :模拟执行权限检查...,目标类是:cn.testSpring.dao.impl.UserDaoImpl@291f18,被织入增强处理的目标方法为:addUser
环绕开始:执行目标方法之前,模拟开启事务...
最终通知:模拟方法结束后的释放资源...
异常通知:出错了/ by zero

基于注解方式的配置


<?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-4.3.xsd
  http://www.springframework.org/schema/aop 
  http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-4.3.xsd">
      <!-- 指定需要扫描的包,使注解生效 -->
      <context:component-scan base-package="com.testSpring" />
      <!-- 启动基于注解的声明式AspectJ支持 -->
      <aop:aspectj-autoproxy />
</beans>
package cn.testSpring.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;
/**
 * 切面类,在此类中编写通知
 */
@Aspect
@Component
public class MyAspect {
	// 定义切入点表达式
	@Pointcut("execution(* cn.testSpring.dao.impl.*.*(..))")
	// 使用一个返回值为void、方法体为空的方法来命名切入点
	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("后置通知:模拟记录日志...," );
		System.out.println("被织入增强处理的目标方法为:"
		              + 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("最终通知:模拟方法结束后的释放资源...");
	}
}
package cn.testSpring.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.testSpring.dao.UserDao;
// 测试类
public class TestAnnotationAspectj {
	public static void main(String args[]) {

		ApplicationContext applicationContext = 
                 new ClassPathXmlApplicationContext("test6.xml");
		// 1 从spring容器获得内容
		UserDao userDao = (UserDao) applicationContext.getBean("userDao");
		// 2 执行方法
		userDao.addUser();
	}
}
结果就不说了,这有几个经典的错误,第一,你的项目中存在同名类,那么注解方式会报错,还有就是配置文件路径不对也会报错,最后切入点表达式@Pointcut("execution(* cn.testSpring.dao.impl.*.*(..))")中的参数设置有问题,程序不能按预期执行。以上两篇博文介绍了spring最基本的内容,在下一篇博文中将介绍SpringJDBC以及事务管理。





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值