spring框架快速入门(Spring框架学习第一天)
1 spring框架的四个基础包
1.Spring框架包Spring 4.3.6版本的框架压缩包,名称为spring-framework-4.3.6.RELEASE-dist.zip,此压缩包可以通过地址“http://repo.spring.io/simple/libs-release-local/org/springframework/spring/4.3.6.RELEASE/”下载。
libs目录中的JAR包分为三类,其中以RELEASE.jar结尾的是Spring框架class文件的JAR包;以RELEASE-Javadoc.jar结尾的是Spring框架API文档的压缩包;以RELEASE-sources.jar结尾的是Spring框架源文件的压缩包。整个Spring框架由20个模块组成,该目录下Spring为每个模块都提供了这三类压缩包。
在libs目录中,有四个Spring的基础包,它们分别对应Spring核心容器的四个模块,具体介绍如下。
· spring-core-4.3.6.RELEASE.jar:包含Spring框架基本的核心工具类,Spring其他组件都要用到这个包里的类,是其他组件的基本核心。
· spring-beans-4.3.6.RELEASE.jar:所有应用都要用到的JAR包,它包含访问配置文件、创建和管理Bean以及进行Inversion of Control(IoC)或者Dependency Injection(DI)操作相关的所有类。
· spring-context-4.3.6.RELEASE.jar:Spring提供了在基础IoC功能上的扩展服务,还提供了许多企业级服务的支持,如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等。
· spring-expression-4.3.6.RELEASE.jar:定义了Spring的表达式语言。
2.第三方依赖包
在使用Spring开发时,除了要使用自带的JAR包外,Spring的核心容器还需要依赖commons.logging的JAR包。该JAR包可以通过网址“http://commons.apache.org/proper/commons-logging/download_logging.cgi”下载。下载完成后,会得到一个名为commons-logging-1.2-bin.zit的压缩包。将压缩包解压到自定义目录后,即可找到commons-logging-1.2.jar。
初学者学习Spring框架时,只需将Spring的4个基础包以及commons-logging-1.2.jar复制到项目的lib目录,并发布到类路径中即可。
3 Spring框架提供了两种核心容器,分别为BeanFactory和ApplicationContext。
1.BeanFactory
创建BeanFactory实例时,需要提供Spring所管理容器的详细配置信息,这些信息通常采用XML文件形式来管理,这种加载方式在实际开发中并不多用,读者了解即可。
2.ApplicationContext
ApplicationContext是BeanFactory的子接口,也被称为应用上下文,是另一种常用的Spring核心容器。它由org.springframework.context. ApplicationContext接口定义,不仅包含了BeanFactory的所有功能,还添加了对国际化、资源访问、事件传播等方面的支持。
1.通过ClassPathXmlApplicationContext创建classpathXmlApplicationContext会从类路径classPath中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,其使用语法如下。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
上述代码中,configLocation参数用于指定Spring配置文件的名称和位置。如果其值为“applicationContext.xml”,则Spring会去类路径中查找名称为applicationContext.xml的配置文件。
2.通过FileSystemXmlApplicationContext创建
FileSystemXmlApplicationContext会从指定的文件系统路径(绝对路径)中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作,其使用语法如下。
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
与ClassPathXmlApplicationContext有所不同的是,在读取Spring的配置文件时,FileSystemXmlApplicationContext不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,例如“D:/workspaces/applicationContext.xml”。如果在参数中写的不是绝对路径,那么方法调用的时候,会默认用绝对路径来找。这种采用绝对路径的方式,会导致程序的灵活性变差,所以这个方法一般不推荐使用。
在使用Spring框架时,可以通过实例化其中任何一个类来创建ApplicationContext容器。通常在Java项目中,会采用通过ClassPathXmlApplicationContext类来实例化ApplicationContext容器的方式,而在Web项目中,ApplicationContext容器的实例化工作会交由Web服务器来完成。Web服务器实例化ApplicationContext容器时,通常会使用基于ContextLoaderListener实现的方式,此种方式只需要在web.xml中添加如下代码。
<! -- 指定Spring配置文件的位置,多个配置文件时,以逗号分隔-->
<context-param> <param-name>contextConfigLocation</param-name>
<! -- Spring将加载spring目录下的applicationContext.xml文件 -->
<param-value> classpath:spring/applicationContext.xml </param-value>
</context-param>
<! -- 指定以ContextLoaderListener方式启动Spring容器 -->
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
4举例:
记得将applicationContext.xml放入src/main/java目录下,同时classpath引入commons-logging,bean,context,core,expression等核心包。
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-4.3.xsd">
<bean id="userDao" class="com.itheima.IoC.UserDaoImpl" />
</beans>
------------------------代码
public interface UserDao {
public void say();
}
public class UserDaoImpl implements UserDao {
public void say() {
// TODO Auto-generated method stub
System.out.println("userDao say Hello World!");
}
}
public class TestIoc {
public static void main(String[] args) {
//初始化spring容器,加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过容器获得UserDao实例
UserDao userDao = (UserDao)applicationContext.getBean("userDao");
userDao.say();
}
}
spring框架的初始化流程即完成了,首先定义接口类,然后定义实现类(javabean),在applicationContext.xml注册javabean接口调用该类的方法。
5 依赖注入的概念
在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转.
6 依赖注入的实现方式
依赖注入的作用就是在使用Spring框架创建对象时,动态地将其所依赖的对象注入Bean组件中,其实现方式通常有两种,一种是属性setter方法注入,另一种是构造方法注入,具体介绍如下。
· 属性setter方法注入:指IoC容器使用setter方法注入被依赖的实例。通过调用无参构造器或无参静态工厂方法实例化Bean后,调用该Bean的setter方法,即可实现基于setter方法的依赖注入。
· 构造方法注入:指IoC容器使用构造方法注入被依赖的实例。基于构造方法的依赖注入通过调用带参数的构造方法来实现,每个参数代表着一个依赖.
setter方法的xml配置:
<! --添加一个id为userService的实例 -->
<bean id="userService" class="com.itheima.ioc.UserServiceImpl">
<! -- 将id为userDao的Bean实例注入到userService实例中 -->
<property name="userDao" ref="userDao" />
</bean>
在上述代码中,<property>是<bean>元素的子元素,它用于调用Bean实例中的setUserDao()方法完成属性赋值,从而实现依赖注入。其name属性表示Bean实例中的相应属性名,ref属性用于指定其属性值。
7 Bean的配置
在Spring中,XML配置文件的根元素是<beans>, <beans>中包含了多个<bean>子元素,每一个<bean>子元素定义了一个Bean,并描述了该Bean如何被装配到Spring容器中。
如果在Bean中未指定id和name,则Spring会将class值当作id使用。
8 Bean的实例化
1.构造器实例化
代码
package com.itheima.instance.constructor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InstanceTest1 {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "com/itheima/instance/constructor/bean1.xml";
// ApplicationContext在加载配置文件时,对Bean进行实例化
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Bean1 bean = (Bean1) applicationContext.getBean("bean1");
System.out.println(bean);
}
}
bean1.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">
<bean id="bean1" class="com.itheima.instance.constructor.Bean1" />
</beans>
构造器实例化只要指定xml路径,定义新的javabean类即可。
9 Bean的作用域
singleton作用域
singleton是Spring容器默认的作用域,当Bean的作用域为singleton时,Spring容器就只会存在一个共享的Bean实例,并且所有对Bean的请求,只要id与该Bean的id属性相匹配,就会返回同一个Bean实例。singleton作用域对于无会话状态的Bean(如Dao组件、Service组件)来说,是最理想的选择。
在Spring配置文件中,Bean的作用域是通过<bean>元素的scope属性来指定的,该属性值可以设置为singleton、prototype、request、session、globalSession、application和websocket七个值,分别表示表2-2中的七种作用域。要将作用域定义成singleton,只需将scope的属性值设置为singleton即可,其示例代码如下
<bean id="scope" class="com.itheima.scope.Scope" scope="singleton"/>
prototype作用域
对需要保持会话状态的Bean(如Struts2的Action类)应该使用prototype作用域。在使用prototype作用域时,Spring容器会为每个对该Bean的请求都创建一个新的实例。
<bean id="scope" class="com.itheima.scope.Scope" scope="prototype" />
10 Bean的装配方式
Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配等(其中最常用的是基于注解的装配)。
使用设值注入时,在Spring配置文件中,需要使用<bean>元素的子元素<property>来为每个属性注入值;而使用构造注入时,在配置文件里,需要使用<bean>元素的子元素<constructor-arg>来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。
<?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">
<bean id="user1" class="com.itheima.assemble.User">
<constructor-arg index="0" value="tom" />
<constructor-arg index="1" value="123456" />
<constructor-arg index="2">
<list>
<value>"constructorvalue1"</value>
<value>"constructorvalue2"</value>
</list>
</constructor-arg>
</bean>
<bean id="user2" class="com.itheima.assemble.User">
<property name="username" value="张三"></property>
<property name="password" value="654321"></property>
<property name="list">
<list>
<value>"setlistvalue1"</value>
<value>"setlistvalue2"</value>
</list>
</property>
</bean>
</beans>
在上述配置文件中,<constructor-arg >元素用于定义构造方法的参数,其属性index表示其索引(从0开始), value属性用于设置注入的值,其子元素<list>来为User类中对应的list集合属性注入值。然后又使用了设值注入方式装配User类的实例,其中<property>元素用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入,而其子元素<list>同样是为User类中对应的list集合属性注入值。
读取配置文件实例
package com.itheima.assemble;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XmlBeanAssembleTest {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "com/itheima/assemble/bean5.xml";
// 加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 构造方式输出结果
System.out.println(applicationContext.getBean("user1"));
// 设值方式输出结果
System.out.println(applicationContext.getBean("user2"));
User user1 = (User)applicationContext.getBean("user1");
System.out.println(user1.toString());
}
}
11 基于Annotation的装配
Spring中定义了一系列的注解,常用的注解如下所示。
· @Component:可以使用此注解描述Spring中的Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
· @Repository:用于将数据访问层(DAO层)的类标识为Spring中的Bean,其功能与@Component相同。
· @Service:通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
· @Controller:通常作用在控制层(如Spring MVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
· @Autowired:用于对Bean的属性变量、属性的setter方法及构造方法进行标注,配合对应的注解处理器完成Bean的自动配置工作。默认按照Bean的类型进行装配。
· @Resource:其作用与Autowired一样。其区别在于@Autowired默认按照Bean类型装配,而@Resource默认按照Bean实例名称进行装配。@Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException异常。
· @Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为按Bean的实例名称装配,Bean的实例名称由@Qualifier注解的参数指定。在上面几个注解中,虽然@Repository、@Service与@Controller功能与@Component注解的功能相同,但为了使标注类本身用途更加清晰,建议在实际开发中使用@Repository、@Service与@Controller分别对实现类进行标注。
beans6.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: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/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.itheima.annotation" />
</beans>
三种注解方式如下:
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
// TODO Auto-generated method stub
System.out.println("userdao....save....");
}
}
@Service("userService")
public class UserServiceImpl implements UserService{
@Resource(name="userDao")
private UserDao userDao;
public void save() {
//调用userDao中的save方法
this.userDao.save();
System.out.println("userservice....save...");
}
}
@Controller("userController")
public class UserController {
@Resource(name="userService")
private UserService userSerivce;
public void save()
{
this.userSerivce.save();
System.out.println("usercontroller....save...");
}
}
调用如下:
// 定义配置文件路径
String xmlPath = "com/itheima/annotation/beans6.xml";
// 加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 获取UserController实例
UserController userController =
(UserController) applicationContext.getBean("userController");
// 调用UserController中的save()方法
userController.save();
上述Spring配置文件中的注解方式虽然较大程度简化了XML文件中Bean的配置,但仍需要在Spring配置文件中一一配置相应的Bean,为此Spring注解提供了另外一种高效的注解配置方式(对包路径下的所有Bean文件进行扫描),其配置方式如下。
<context:component-scan base-package="Bean所在的包路径"/>
Spring 4.0以上版本使用上面的代码对指定包中的注解进行扫描前,需要先向项目中导入Spring AOP的JAR包spring-aop-4.3.6.RELEASE.jar,否则程序在运行时会报出“java.lang.NoClassDefFound Error:org/springframework/aop/TargetSource”错误。
12 自动装配
Spring的<bean>元素中包含一个autowire属性,我们可以通过设置autowire的属性值来自动装配Bean。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中。
12 什么是AOP
AOP的全称是Aspect-Oriented Programming,即面向切面编程(也称面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。为了解决这一问题,AOP思想随之产生。
AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。
AOP的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
13 JDK动态代理
JDK动态代理是通过java.lang.reflect.Proxy类来实现的,我们可以调用Proxy类的newProxyInstance()方法来创建代理对象。对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。
实现过程如下:
1.定义要被代理类的接口和实现,如UserDao,UserDaoImpl。
2.定义切面类,比如在该类中定义一个模拟权限检查的方法和一个模拟记录日志的方法,这两个方法就表示切面中的通知,如MyAspect。
3.创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理。
/**
* 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;
}
}
JdkProxy类实现了InvocationHandler接口,并实现了接口中的invoke()方法,所有动态代理类所调用的方法都会交由该方法处理。在创建的代理方法createProxy()中,使用了Proxy类的newProxyInstance()方法来创建代理对象。newProxyInstance()方法中包含3个参数,其中第1个参数是当前类的类加载器,第2个参数表示的是被代理对象实现的所有接口,第3个参数this代表的就是代理类JdkProxy本身。在invoke()方法中,目标类方法执行的前后,会分别执行切面类中的check_Permissions()方法和log()方法。
4 调用方法
// 创建代理对象
JdkProxy jdkProxy = new JdkProxy();
// 创建目标对象
UserDao userDao= new UserDaoImpl();
// 从代理对象中获取增强后的目标对象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
// 执行方法
userDao1.addUser();
userDao1.deleteUser();
如上所述,对目标类UserDao进行了增强,通过代理类实现了MyAspect中的方法。
14 CGLIB代理
JDK动态代理的使用非常简单,但它还有一定的局限性——使用动态代理的对象必须实现一个或多个接口。如果要对没有实现接口的类进行代理,那么可以使用CGLIB代理。
CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类进行增强。在Spring的核心包中已经集成了CGLIB所需要的包,所以开发中不需要另外导入JAR包。
cglib地址——https://github.com/cglib/cglib,下载地址https://nowjava.com/jar/search/cglib
15 AspectJ开发
基于XML的声明式AspectJ
1.配置切面
在Spring的配置文件中,配置切面使用的是<aop:aspect>元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean(如上述代码中定义的myAspect)。定义完成后,通过<aop:aspect>元素的ref属性即可引用该Bean。配置<aop:aspect>元素时,通常会指定id和ref两个属性
2.配置切入点
在Spring的配置文件中,切入点是通过<aop:pointcut>元素来定义的。当<aop:pointcut>元素作为<aop:config>元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当<aop:pointcut>元素作为<aop:aspect>元素的子元素时,表示该切入点只对当前切面有效。在定义<aop:pointcut>元素时,通常会指定id和expression两个属性。
execution(* com.itheima.jdk.*.*(..))就是定义的切入点表达式,该切入点表达式的意思是匹配com.itheima.jdk包中任意类的任意方法的执行。其中execution()是表达式的主体,第1个*表示的是返回类型,使用*代表所有类型;com.itheima.jdk表示的是需要拦截的包名,后面第2个*表示的是类名,使用*代表所有的类;第3个*表示的是方法名,使用*表示所有方法;后面(..)表示方法的参数,其中的“..”表示任意参数。需要注意的是,第1个*与包名之间有一个空格。
3.配置通知
在配置代码中,分别使用<aop:aspect>的子元素配置了5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性
举例——
(1)导入AspectJ框架相关的JAR包,具体如下。
· spring-aspects-4.3.6.RELEASE.jar:Spring为AspectJ提供的实现,Spring的包中已经提供。
· aspectjweaver-1.8.10.jar:是AspectJ框架所提供的规范,读者可以通过网址“http://mvnrepository.com/artifact/org.aspectj/aspectjweaver/1.8.10”下载。
(2)创建切面类MyAspect
package com.itheima.aspect.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("最终通知:模拟方法结束后的释放资源...");
}
}
以上文件中分别定义了5种不同类型的通知,在通知中使用了JoinPoint接口及其子接口ProceedingJoinPoint作为参数来获得目标对象的类名、目标方法名和目标方法参数等。需要注意的是,环绕通知必须接收一个类型为ProceedingJoinPoint的参数,返回值也必须是Object类型,且必须抛出异常。异常通知中可以传入Throwable类型的参数来输出异常信息。
(3)创建配置文件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-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="userDao" class="com.itheima.jdk.UserDaoImpl" />
<bean id="myAspect" class="com.itheima.aspect.xml.MyAspect" />
<!--3 aop编程 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="myAspect">
<!--3.1 配置切入点,通知最后增强哪些方法 -->
<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>
(4)测试调用
package com.itheima.aspect.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;
// 测试类
public class TestXmlAspectj { public static void main(String args[]) {
String xmlPath = "com/itheima/aspect/xml/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 1 从spring容器获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 2 执行方法
userDao.addUser();
}
}
基于注解的声明式AspectJ
具体实现如下:
1.定义bean
package com.itheima.jdk;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void addUser() {
// TODO Auto-generated method stub
System.out.println("添加用户");
}
public void deleteUser() {
// TODO Auto-generated method stub
System.out.println("删除用户");
}
}
2.定义切面类
package com.itheima.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;
/**
* 切面类,在此类中编写通知
*
*/
@Aspect
@Component
public class MyAspect {
// 定义切入点表达式
@Pointcut("execution(* com.itheima.jdk.*.*(..))")
// 使用一个返回值为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());
}
/**
* 环绕通知 ProceedingJoinPoint是JoinPoint子接口,表示可以执行目标方法 1.必须是Object类型的返回值
* 2.必须接收一个参数,类型为ProceedingJoinPoint 3.必须throws Throwable
*/
// 环绕通知
@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("最终通知:模拟方法结束后的释放资源...");
}
}
3定义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-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.itheima" />
<!-- 启动基于注解的声明式AspectJ支持 -->
<aop:aspectj-autoproxy />
</beans>
4 测试实现
package com.itheima.aspectj.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.itheima.jdk.UserDao;
// 测试类
public class TestAnnotationAspectj {
public static void main(String args[]) {
String xmlPath = "com/itheima/aspectj/annotation/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 1 从spring容器获得内容
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
// 2 执行方法
userDao.addUser();
}
}
首先使用@Aspect注解定义了切面类,由于该类在Spring中是作为组件使用的,所以还需要添加@Component注解才能生效。然后使用了@Poincut注解来配置切入点表达式,并通过定义方法来表示切入点名称。接下来在每个通知相应的方法上添加了相应的注解,并将切入点名称“myPointCut”作为参数传递给需要执行增强的通知方法。如果需要其他参数(如异常通知的异常参数),可以根据代码提示传递相应的属性值。
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com