1、Spring定义
1.1 百度摘要
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。(由Rod Johnson创建的一个开源框架)
1.2 Spring的一些优点
- 方便解耦,简化开发
- 核心IOC与DI的思想
- AOP编程的支持
- 声明式的事物支持
- 包容性,能够集成各种优秀的框架
- 降低JavaEE API的使用难度
- 方便程序的测试
- Spring属于低侵入,代码污染极低
1.3 Spring的各个包
附带:【Spring的各个包的作用】
Spring AOP:Spring的面向切面编程,提供AOP(面向切面编程)的实现
Spring Aspects:Spring提供的对AspectJ框架的整合
Spring Beans:Spring IOC的基础实现,包含访问配置文件、创建和管理bean等。
Spring Context:在基础IOC功能上提供扩展服务,此外还提供许多企业级服务的支持,有邮件服务、任务调度、JNDI定位,EJB集成、远程访问、缓存以及多种视图层框架的支持。
spring-context-indexer:
Spring Context Support:Spring context的扩展支持,用于MVC方面。
Spring Core:Spring的核心工具包
Spring expression:Spring表达式语言
spring-framework-bom:统一管理jar包版本
Spring Instrument:Spring对服务器的代理接口
Spring Instrument Tomcat:Spring对tomcat连接池的集成
Spring jcl : JCL采用了设计模式中的“适配器模式”,它对外提供统一的接口,然后在适配类中将对日志的操作委托给具体的日志框架,比如Log4J,Java Logging API等
Spring JDBC:对JDBC 的简单封装
Spring JMS:为简化jms api的使用而做的简单封装
Spring Messaging:集成messaging api和消息协议提供支持。
Spring orm:整合第三方的orm实现,如hibernate,ibatis,jdo以及spring 的jpa实现
Spring oxm:Spring对于object/xml映射的支持,可以让JAVA与XML之间来回切换
Spring test:对JUNIT等测试框架的简单封装
Spring tx:为JDBC、Hibernate、JDO、JPA等提供的一致的声明式和编程式事务管理。
Spring web:包含Web应用开发时,用到Spring框架时所需的核心类,包括自动载入WebApplicationContext特性的类、Struts与JSF集成类、文件上传的支持类、Filter类和大量工具辅助类。
Spring webmvc:包含SpringMVC框架相关的所有类。包含国际化、标签、Theme、视图展现的FreeMarker、JasperReports、 Tiles、Velocity、XSLT相关类。当然,如果你的应用使用了独立的MVC框架,则无需这个JAR文件里的任何类。
Spring webmvc portlet:Spring MVC的增强
Spring websocket:提供 Socket通信, web端的推送功能
2、Spring 入门案例
2.1 几个小案例
2.1.1 包管理
使用maven管理
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-version>5.1.3.RELEASE</spring-version>
</properties>
<dependencies>
<!-- 是apache最早提供的日志的门面接口。提供简单的日志实现以及日志解耦功能 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-expression -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.1.2 IOC的入门小例子
IoC(Inversion of Control,控制反转)。
所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
简单来说:初始化一个对象,不再是使用传统的new方式,而是需要的时候,从Spring的容器中取。
- 接口类
public interface UserService {
void addUser();
void updateUser(String code);
void delUser(String code);
User findUserAll();
}
- 实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("进入了addUser的方法.............");
}
@Override
public void updateUser(String code) {
System.out.println("进入了updateUser的方法.............");
}
@Override
public void delUser(String code) {
System.out.println("进入了delUser的方法.............");
}
@Override
public User findUserAll() {
System.out.println("进入了findUserAll的方法.............");
User user = null;
return user;
}
}
- 配置文件
<?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.xsd">
<!-- 配置service
<bean> 配置需要创建的对象
id :用于之后从spring容器获得实例时使用的 【组件扫描的情况:默认的id号或者bean的name是类名的首字母小写。】
class :需要创建实例的全限定类名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl"></bean>
</beans>
测试类
public class UserTest {
/**
* 测试通过Spring的容器获取bean
*/
@Test
public void test() {
//获取Spring的容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
//获得bean .不是传统的需要new 出来,只需要通过Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
2.1.3 DI 的入门小例子
- Dao 接口类
public interface UserDao {
int saveUser(User user);
int updateUser(String code);
int delUser(String code);
User findUser();
}
- Dao 实现类
public class UserDaoImpl implements UserDao {
@Override
public int saveUser(User user) {
System.out.println("进入了Dao层的saveUser的方法.............");
return 0;
}
@Override
public int updateUser(String code) {
System.out.println("进入了Dao层的updateUser的方法.............");
return 0;
}
@Override
public int delUser(String code) {
System.out.println("进入了Dao层的delUser的方法.............");
return 0;
}
@Override
public User findUser() {
System.out.println("进入了Dao层的findUser的方法.............");
return null;
}
}
- Service接口类
使用IOC例子中的接口
- Service接口类实现类
public class UserServiceImpl implements UserService {
/**
* 注入的对象
*/
private UserDao userDao;
/**
* 注入对象,需要使用到setXXX方法
* @param userDao
*/
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void addUser() {
userDao.saveUser(new User());
System.out.println("进入了addUser的方法.............");
}
@Override
public void updateUser(String code) {
System.out.println("进入了updateUser的方法.............");
}
@Override
public void delUser(String code) {
System.out.println("进入了delUser的方法.............");
}
@Override
public User findUserAll() {
System.out.println("进入了findUserAll的方法.............");
User user = null;
return user;
}
}
- 配置文件
<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.xsd">
<bean id="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!-- 配置service
<bean> 配置需要创建的对象
id :用于之后从spring容器获得实例时使用的 【组件扫描的情况:默认的id号或者bean的name是类名的首字母小写。】
class :需要创建实例的全限定类名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
<!--使用property进行对象注入
name :bean的属性名
ref : 指向具体的实现对象引用
-->
<property name="userDao" ref="userDaoId"></property>
</bean>
</beans>
- 测试例子
public class UserTest {
/**
* 测试通过Spring的容器获取bean
*/
@Test
public void test() {
//获取Spring的容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
//获得bean .不是传统的需要new 出来,只需要通过Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
}
}
- 结果
2.1.4 依赖注入装配Bean 属性
- 依赖注入方式:构造器注入和setter注入
- 装配方式(创建应用对象之间协作关系的行为称为装配):手动装配与自动装配
2.1.4.1 基于bean的注入
- bean对象 :User
public class User {
public int age;
String code;
public String name;
public User() {
}
public User(int age, String code, String name) {
this.age = age;
this.code = code;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Person {
private User user;
private String unicode ;
public Person() {
}
public Person(User user, String unicode) {
this.user = user;
this.unicode = unicode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getUnicode() {
return unicode;
}
public void setUnicode(String unicode) {
this.unicode = unicode;
}
@Override
public String toString() {
return "Person{" +
"user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
"】, unicode='" + unicode + '\'' +
'}';
}
}
spring的xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoId" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!-- 配置service
<bean> 配置需要创建的对象
id :用于之后从spring容器获得实例时使用的 【组件扫描的情况:默认的id号或者bean的name是类名的首字母小写。】
class :需要创建实例的全限定类名
-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl">
<!--使用property进行对象注入
name :bean的属性名
ref : 指向具体的实现对象引用
-->
<property name="userDao" ref="userDaoId"></property>
</bean>
<!--基于构造器方法注入
<constructor-arg> 用于配置构造方法一个参数argument
name :参数的名称
value:设置普通数据
ref:引用数据,一般是另一个bean id值
index :参数的索引号,从0开始 。如果只有索引,匹配到了多个构造方法时,默认使用第一个。
type :确定参数类型
-->
<bean id="user" class="com.mall.spring.bean.User">
<!--方式1-->
<!-- <constructor-arg name="age" value="11" />
<constructor-arg name="code" value="00001" />
<constructor-arg name="name" value="张三" />-->
<!--方式2-->
<constructor-arg index="0" type="int" value="12"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" value="00001"></constructor-arg>
<constructor-arg index="2" type="java.lang.String" value="张三"></constructor-arg>
</bean>
<!--基于setter方法
* 普通数据
<property name="" value="值">
等效
<property name="">
<value>值
等效
<property p:属性=值 />
* 引用数据
<property name="" ref="另一个bean">
等效
<property name="">
<ref bean="另一个bean"/>
-->
<bean id="userOther" class="com.mall.spring.bean.User" p:name="李四">
<property name="age" value="13"></property>
<property name="code">
<value>00002</value>
</property>
</bean>
<bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
<property name="user" ref="userOther"></property>
</bean>
</beans>
- 测试类
@Test
public void testBean() {
User user = (User) applicationContext.getBean("user");
System.out.println("user = " + user);
User userOther = (User) applicationContext.getBean("userOther");
System.out.println("userOther = " + userOther);
Person person = (Person) applicationContext.getBean("person");
System.out.println("person = " + person);
}
结果:
2.1.4.2 基于集合的注入
- 定义bean
public class Person {
private User user;
private String unicode ;
private String[] arrData ;
private List list;
private Set set;
private Map map;
public Set getSet() {
return set;
}
public void setSet(Set set) {
this.set = set;
}
public Map getMap() {
return map;
}
public void setMap(Map map) {
this.map = map;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
public String[] getArrData() {
return arrData;
}
public void setArrData(String[] arrData) {
this.arrData = arrData;
}
public Person() {
}
public Person(User user, String unicode) {
this.user = user;
this.unicode = unicode;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getUnicode() {
return unicode;
}
public void setUnicode(String unicode) {
this.unicode = unicode;
}
@Override
public String toString() {
return "Person{" +
"user=【" + user.getCode() + "," + user.getAge() + "," + user.getName() +
"】, unicode='" + unicode + '\'' +
'}';
}
}
- 配置文件
<bean id="person" class="com.mall.spring.bean.Person" p:unicode="00000001" >
<property name="user" ref="userOther"></property>
<!--数组的注入-->
<property name="arrData">
<array>
<value>Dene</value>
<value>Denr</value>
<value>Dent</value>
<value>Deny</value>
<value>Denu</value>
<value>Deno</value>
</array>
</property>
<!--List集合的注入-->
<property name="list">
<list>
<value>12</value>
<value>你好</value>
<value>是的</value>
<value>我不是</value>
<value>你就是</value>
</list>
</property>
<!--Set 集合的注入-->
<property name="set">
<set>
<value>121</value>
<value>你好1</value>
<value>是的1</value>
<value>我不是1</value>
<value>你就是1</value>
</set>
</property>
<!-- Map集合 -->
<property name="map">
<map>
<entry key="name1" value="Lucy"></entry>
<entry key="name2" value="Jack"></entry>
<entry>
<key><value>name3</value></key>
<value>李白</value>
</entry>
</map>
</property>
</bean>
- 测试类
@Test
public void testBean() {
User user = (User) applicationContext.getBean("user");
System.out.println("user = " + user);
User userOther = (User) applicationContext.getBean("userOther");
System.out.println("userOther = " + userOther);
Person person = (Person) applicationContext.getBean("person");
System.out.println("person = " + person);
System.out.println("person 中的数组 = " + Arrays.toString(person.getArrData()));
System.out.println("person 中的数组 = " + Arrays.toString(person.getArrData()));
System.out.println("person 中的List数组 = " + person.getList());
System.out.println("person 中的Map = " + person.getMap());
}
测试结果
结论:
上面所提到的都是手动装配的方式。当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化,这就是装配。如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了(**配置文件就会很臃肿 **)。基于这种场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。与自动装配配合的还有“自动检测”,这 个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依赖注入“自动化”的一个简化配置的操作。
2.1.5 装配方式
2.1.5.1 装配分四种:byName, byType, constructor, autodetect。 【可以使用autowire进行配置,不配置,默认使用byName】
比如:
配置文件:
<bean id="userDao" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<bean id="userDaoII" class="com.mall.spring.dao.impl.UserDaoImpl"></bean>
<!--使用 property 进行装配-->
<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" >
<property name="userDao" ref="userDaoll"></property>
</bean>
-
byName:根据属性名自动装配。此选项将检查容器并根据名字查找 ,与属性完全一致的bean,并将其与属性自动装配。
例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byName" />
-
byType 如果容器中存在一个与指定属性类型相同的bean,那么将与 该属性自动装配;如果存在多个该类型bean,那么抛出异常, 并指出不能使用byType方式进行自动装配;如果没有找 到相匹配的bean,则什么事都不发生,也可以通过设置 。
例子:<bean id="userService" class="com.mall.spring.service.impl.UserServiceImpl" autowire="byType">
-
constructor 就是通过构造器来将类型与参数相同的bean进行装配。
-
autodetect 是constructor与byType的组合,会先进行constructor,如果不成功,再进行byType。
2.1.5.2 注解开启与一些自动装配的注解
- 开启注解 :
<context:annotation-config />
- 扫描注解包路径:
<context:component-scan base-package="com.mall.spring" />
2.1.5.2.1 常用的自动装配注解有以下几种:@Autowired,@Resource,@Inject,@Qualifier,@Named
- @Autowired 使用
解释:@Autowired注解是byType类型的,这个注解可以用在属性上面,setter方面上面以及构造器上面。使用这个注解时,就不需要在类中为属性添加setter方法了。但是这个属性是强制性的,也就是说必须得装配上,如果没有找到合适的bean能够装配上,就会抛出异常。
这时可以使用required=false来允许可以不被装配上,默认值为true。
当required=true时,@Autowired要求必须装配,但是在没有bean能装配上时,就会抛出异常:NoSuchBeanDefinitionException,如果required=false时,则不会抛出异常。 - @Qualifier注解
解释:@Qualifier注解使用byName进行装配,这样可以在多个类型一样的bean中,明确使用哪一个名字的bean来进行装配。@Qualifier注解起到了缩小自动装配候选bean的范围的作用,@Qualifier不能单独使用。
代码展示:
@Autowired
@Qualifier(value = "animal")
private Animal animal;
- @Resource 注解
解释:@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。(注解也是java ee的)
如代码:
@Resource(name = "person")
private Person person;
@Resource
private User user;
- @Inject注解
解释:与@Autowired注解作用一样,也是byType类型,而且是java ee提供的,完全可以代替@Autowired注解,但是@Inject必须是强制装配的,没有required属性,也就是不能为null,如果不存在匹配的bean,会抛出异常。
@Inject也有一个组合的注解,就是@Named注解,与@Qualifier作用一样,也是byName,但是不是spring的,是java ee标准的。这样就出现了两套自动装配的注解组合,@Autowired与@Qualifier是spring提供的,@Inject与@Named是java ee的
2.1.5.2.2 构造器注解:@Controller,@Components,@Service,@Repository和使用@Component标注的自定义注解
作用:生成的bean的ID默认为类的非限定名,也就是把类的名字的首字母换成小写。可以在这些注解的值中写名bean id的值,比如:@Component(value = "animal")。
几个注解的简单说明:
@Controller注解 只能用控制器类上
@Service注解 只能用在业务类上
@Repository注解 只能用在dao类上
@Component注解 无法按照上面三个注解分类,就用此注解
3、Spring AOP 切面
3.1 概念
3.1.1定义:
定义:
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
3.1.2 基本概念图
3.1.3 通知类型介绍
介绍
(1)Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可
(2)AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值
(3)AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名
来访问目标方法中所抛出的异常对象
(4)After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式
(5)Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint。
3.2 案例
3.2.1 增加包
使用spring的包:
<!-- spring-aop 依赖 start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<!--处理事务和AOP所需的包 切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- spring-aop 依赖 end -->
或许:
<!-- spring-aop 依赖 start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring-version}</version>
</dependency>
<!-- aspectjweaver 处理事务和AOP所需的包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
aopalliance Spring AOP 的接口支持 三个主要业务实体:Advice 、Interceptor、Joinpoint
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- spring-aop 依赖 end -->
3.2.2 基于XML的切面小案例
- 切入类:
public class TimeHandler {
public void printTime() {
System.out.println("当前时间是:" + System.currentTimeMillis());
}
}
- 接口类:前面例子的接口类 【interface UserService】
- 实现类: 前面例子的类 【class UserServiceImpl implements UserService】
- 配置文件:
<aop:config>
<aop:aspect id = "time" ref="timeHandler">
<!--切入Service的所有方法-->
<aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<!--切入Service的以add开头的方法-->
<!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
<aop:before method="printTime" pointcut-ref="timeMethod" />
<aop:after method="printTime" pointcut-ref="timeMethod" />
</aop:aspect>
</aop:config>
- 测试类
@Test
public void test() {
//获得bean .不是传统的需要new 出来,只需要通过Spring容器中取
UserService userService = (UserService) applicationContext.getBean("userService");
userService.addUser();
System.out.println("------------------------------------------------------------------");
userService.updateUser("name");
}
- 运行结果:
3.2.3 基于XML的切面小案例增加横切点,关键字 order 使用
order属性的数字就是横切关注点的顺序
- 新增日志模拟类:
public class LogHamdler {
public void logBefore() {
System.out.println("日志打印前.....before.....");
}
public void logAfter() {
System.out.println("日志打印后.....after.....");
}
}
- xml配置
<aop:config>
<aop:aspect id = "time" ref="timeHandler" order="1">
<!--切入Service的所有方法-->
<aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<!--切入Service的以add开头的方法-->
<!-- <aop:pointcut id="timeMethod" expression="execution(* com.mall.spring.service.UserService.add*(..))" />-->
<aop:before method="printTime" pointcut-ref="timeMethod" />
<aop:after method="printTime" pointcut-ref="timeMethod" />
</aop:aspect>
<aop:aspect id = "log" ref="logHandler" order="2">
<aop:pointcut id="logMethod" expression="execution(* com.mall.spring.service.UserService.*(..))" />
<aop:before method="logBefore" pointcut-ref="logMethod" />
<aop:after method="logAfter" pointcut-ref="logMethod" />
</aop:aspect>
</aop:config>
- 测试类运行后结果得到
3.2.4 基于注解方式小案例
- 配置文件
<!--配置自动匹配 aspectJ 注解的 Java 类生成代理对象 -->
<aop:aspectj-autoproxy />
- 接口类:
public interface PersonService {
Person addPerson(Person person);
void delPerson(String code);
}
- 接口实现类
@Service("personService")
public class PersonServiceImpl implements PersonService {
@Resource(name = "person")
private Person person;
@Resource
private User user;
@Override
public Person addPerson(Person person) {
System.out.println("进入了PersonServiceImpl类的addPerson()...............");
return person;
}
@Override
public void delPerson(String code) {
int i = 10;
int count = i / 0 ;
System.out.println("进入了PersonServiceImpl类的delPerson()...............");
}
}
- 切面类 一:
package com.mall.spring.config;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* @author 超
* Create by fengc on 2018/12/2 22:54
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(* com.mall.spring.service.PersonService.*(..))")
public void declareJointPointExpression(){}
/**
* 在 com.mall.spring.service.PersonService 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
Object object = joinPoint.getTarget();
System.out.println("The object = " +object+ ",The method = " + methodName + ", begins with = " + Arrays.asList(args) );
}
/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的! result
*/
@AfterReturning(value = "declareJointPointExpression()",returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/* @Around("execution(* com.mall.spring.service.PersonService.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}*/
}
- 切面类二 【为了测试 order关键字 order越小,横切关注点的顺序越先】
@Order(1)
@Aspect
@Component
public class VlidationAspect {
@Before("com.mall.spring.config.LoggingAspect.declareJointPointExpression()")
public void validateArgs(JoinPoint joinPoint) {
System.out.println("-------validateArgs:【Arrays.asList(joinPoint.getArgs())】:" + Arrays.asList(joinPoint.getArgs()));
}
}
测试类:
package com.mall.spring.service;
import com.mall.spring.bean.Person;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 超
* Create by fengc on 2018/12/2 23:06
*/
public class PersonTest {
//获取Spring的容器
ApplicationContext applicationContext = null;
@Before
public void getApplicationContextInstance() {
applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
}
@Test
public void test() {
PersonService personService = (PersonService) applicationContext.getBean("personService");
personService.addPerson(new Person());
System.out.println("---------------------------------------------------------------------------");
personService.delPerson("21321");
}
}
运行结果:
总结论
上面总结了Spring的一些基本用法,当做笔记。
总的来说,Spring是一个十分优秀的框架。它颠覆了我们对编程的一些传统观念。
上面主要让大家了解了Spring的主要核心分别是:Spring的IOC(控制反转)和DI(依赖注入),
Spring的AOP 切面的简单效果。