目录
文章目录
前言
Spring 是轻量级开源的JavaEE框架,它可以解决企业应用开发的复杂性。Spring 有两个核心部分分别是 控制反转(Inversion of Control 简称:IOC)、面向切面编程(Aspect Oriented Programming 简称:Aop),所谓的IOC则是把创建对象的过程交给 Spring 进行管理。而AOP则是对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
下载Spring 依赖
- 首先进入Spring官网找到Projects下的Spring Framework。
-
在Spring Framework中可以查看Spring GA 稳定版本;进入Github。
-
在Github中找到Access to Binaries里的 Spring Framework Artifacts点击。
-
找到Downloading a Distribution点击 https://repo.spring.io地址。
-
找到release/org/springframework/spring,复制的路径拼接到repo.spring.io后面:https://repo.spring.io/release/org/springframework/
-
下载所需的spring版本
导入Spring相关jar包
commons-logging.jar 可以在mvnrepository上下载
IOC 容器
所谓的IOC则是把创建对象的过程交给 Spring 进行管理,实现低耦合。
-
IOC底层原理:xml解析、工厂模式、反射。
-
Spring提供IOC容器实现两接口方式:
-
BeanFactory:是 Spring 内部的使用接口,不推荐开发人员使用 。加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
-
ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,推荐开发人 员使用,加载配置文件时就会把配置文件中的所有对象进行创建。
-
FileSystemXmlApplicationContext(String str) 实现类:某磁盘内的文件地址。
-
ClassPathXmlApplicationContext(String str)实现类:src下某个文件名。
ApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("bean1.xml");
-
-
IOC 容器Bean的操作
Bean管理操作有两种方式
Bean管理有两个操作:Spring 创建对象、Spring 注入属性。
xml配置文件中<bean>
标签介绍
属性 | 描述 |
---|---|
id | 是一个 Bean 的唯一标识符,Spring 容器对 Bean 的配置和管理都通过该属性完成 |
name | Spring 容器同样可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,使用类的全限定名 |
scope | 用于设定 Bean 实例的作用域,其属性值有 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg | <bean> 元素的子元素,可以使用此元素传入构造参数进行实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型 |
property | <bean> 元素的子元素,用于调用 Bean 实例中的 Set 方法完成属性赋值,从而完成依赖注入。该元素的 name 属性指定 Bean 实例中的相应属性名 |
ref | <property> 和<constructor-arg> 等元素的子元索,该元素中的 bean 属性用于指定对 Bean 工厂中某个 Bean 实例的引用 |
value | <property> 和 <constractor-arg> 等元素的子元素,用于直接指定一个常量值 |
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型属性的依赖注入 |
map | 用于封装 Map 类型属性的依赖注入 |
entry | <map> 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值 |
-
<property>
标签属性介绍:(bean子标签)属性 详情 name Java类中所对应的属性名称。 value 要赋值的属性。 ref xml中bean标签的id,也就是xml文件中的一个对象。 -
<constructor-arg>
标签有参构造器介绍:(bean子标签)属性 详情 name 有参构造器中对应的名称。 value 要赋值的属性。 index 有参构造器中第几个参数。
Spring DI 的实现方式:属性注入和构造注入Book类: (依赖注入)
public class Book {
private String name;
public Book(String name) {this.name = name;}
public void setName(String name) {this.name = name;}
public String getName() {return name;}
}
xml文件:
<bean id="user" class="com.tmsklst.spring5.Book"></bean>
-
使用setter方法注入属性。
<bean id="book" class="com.tmsklst.spring5.Book"> <property name="name" value="托马斯·克里斯特"></property> </bean>
-
使用有参构造器注入属性。
<bean id="book" class="com.tmsklst.spring5.Book"> <constructor-arg name="name" value="托马斯·克里斯特"></constructor-arg> </bean>
-
字面量设置null值
<bean id="book" class="com.tmsklst.spring5.Book"> <property name="name"> <null/> </property> </bean>
-
属性值包含特殊符号
<bean id="book" class="com.tmsklst.spring5.Book"> <property name="name"> <value><![CDATA[<<夏洛克·福尔摩斯>>]]></value> </property> </bean>
Spring 实例化:bean(service与dao为案例)
-
创建UserDaoImpl类:
public class UserDaoImpl implements UserDao { @Override public void update() { System.out.println("userDaoImpl update............."); } }
-
创建UserServiceImpl类:(注意:需要个属性添加set方法)
public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public void add() { System.out.println("UserServiceImpl add.............."); userDao.update(); } }
-
创建bean.xml配置文件:
<!-- service对象的创建 --> <bean id="UserService" class="com.tmsklst.service.impl.UserServiceImpl"> <!-- 注入service对象 --> <property name="userDao" ref="UserDao"></property> </bean> <!-- dao对象的创建 --> <bean id="UserDao" class="com.tmsklst.dao.impl.UserDaoImpl"></bean>
-
测试:
public void newInstanceTest() { //加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); //获取配置文件对象 UserServiceImpl userService = context.getBean("UserService", UserServiceImpl.class); userService.add(); }
Spring 实例化:嵌套bean与级联赋值创建对象(一对多为案例)
-
创建部门类Dept:
public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } @Override public String toString() { return "Dept{" +"dname='" + dname + '\'' + '}'; } }
-
创建员工类Emp:
public class Emp { private String ename; private String gender; private Dept dept;//员工属于一个部门,使用对象形式表示 public Dept getDept() { //级联赋值所需的get方法 return dept; } public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } public void setDept(Dept dept) { this.dept = dept; } public void add() { System.out.println(ename + "::" + gender + "::" + dept); } }
-
创建bean.xml配置文件:(嵌套)
<bean id="emp" class="com.tmsklst.pojo.Emp"> <!-- 普通属性 --> <property name="ename" value="托马斯·克里斯特"></property> <property name="gender" value="男"></property> <!-- 对象属性 --> <property name="dept"> <bean id="dept" class="com.tmsklst.pojo.Dept"> <property name="dname" value="技术部"></property> </bean> </property> </bean>
或 (级联赋值 注意:在emp类中一定要生成dept属性的get方法)
<bean id="emp" class="com.tmsklst.pojo.Emp"> <!-- 普通属性 --> <property name="ename" value="托马斯·克里斯特"></property> <property name="gender" value="男"></property> <!-- 级联赋值 --> <property name="dept" ref="dept"></property> <property name="dept.dname" value="财务部"></property> </bean> <bean id="dept" class="com.tmsklst.pojo.Dept"></bean>
-
测试:
public void newInstanceTest() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Emp emp = context.getBean("emp", Emp.class); emp.add(); }
Spring 实例化:集合数据注入
-
创建Student类:
public class Student { //数组属性 private String[] array; //List属性 private List<String> list; //set属性 private Set<String> set; //Map属性 private Map<String,String> map; //List对象属性 private List<User> userList; public void setArray(String[] courses) { this.array = courses; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setUserList(List<User> userList) { this.userList = userList; } public void collectionToString() { System.out.println("array:" + Arrays.asList(array)); System.out.println("list:" + list); System.out.println("set:" + set); System.out.println("map:" + map); System.out.println("userList:" + userList); } }
-
创建xml配置文件:
<!-- 集合属性的注入 --> <bean id="student" class="com.tmsklst.collection.Student"> <!-- 数组注入 --> <property name="array"> <array> <value>array01</value> <value>array02</value> </array> </property> <!-- List注入 --> <property name="list"> <list> <value>list01</value> <value>list02</value> </list> </property> <!-- set注入 --> <property name="set"> <set> <value>set01</value> <value>set02</value> </set> </property> <!-- map注入 --> <property name="map"> <map> <entry key="key01" value="value01"></entry> <entry key="key02" value="value02"></entry> </map> </property> <!-- 对象list注入 --> <property name="userList"> <list> <ref bean="user1"></ref> <ref bean="user2"></ref> </list> </property> </bean> <bean id="user1" class="com.tmsklst.pojo.User"> <property name="name" value="托马斯·克里斯特"></property> </bean> <bean id="user2" class="com.tmsklst.pojo.User"> <property name="name" value="汤姆"></property> </bean>
Spring 实例化:提取List集合、Map对象的数据注入
-
创建Book类:
public class Book { private List<String> list; private Map<Integer,User> map; public void setList(List<String> list) { this.list = list; } public void setMap(Map<Integer, User> map) { this.map = map; } public void add() { System.out.println("list:" + list); System.out.println("map:" + map); } }
-
在配置文件beans头部标签中引入名称空间 util:
<beans xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd>
-
使用 util 标签完成 list 集合与map集合对象的注入提取:
<!-- 创建user对象 --> <bean id="user1" class="com.tmsklst.pojo.User"> <property name="name" value="托马斯·克里斯特"></property> </bean> <bean id="user2" class="com.tmsklst.pojo.User"> <property name="name" value="汤姆"></property> </bean> <!-- 1.提取list集合类型属性注入 --> <util:list id="bookList"> <value>九阳神功</value> <value>九阴正经</value> </util:list> <util:map id="bookMap"> <entry key="1"> <ref bean="user1"></ref> </entry> <entry key="2"> <ref bean="user2"></ref> </entry> </util:map> <!-- 2.提取list集合类型属性注入的使用 --> <bean id="book" class="com.tmsklst.pojo.Book"> <property name="list" ref="bookList"></property> <property name="map" ref="bookMap"></property> </bean>
完整配置文件:
<?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:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- 创建user对象 --> <bean id="user1" class="com.tmsklst.pojo.User"> <property name="name" value="托马斯·克里斯特"></property> </bean> <bean id="user2" class="com.tmsklst.pojo.User"> <property name="name" value="汤姆"></property> </bean> <!-- 1.提取list集合类型属性注入 --> <util:list id="bookList"> <value>九阳神功</value> <value>九阴正经</value> </util:list> <util:map id="bookMap"> <entry key="1"> <ref bean="user1"></ref> </entry> <entry key="2"> <ref bean="user2"></ref> </entry> </util:map> <!-- 2.提取list集合类型属性注入的使用 --> <bean id="book" class="com.tmsklst.pojo.Book"> <property name="list" ref="bookList"></property> <property name="map" ref="bookMap"></property> </bean> </beans>
-
测试
public void collectionTest2() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Book book = context.getBean("book", Book.class); book.add(); }
Spring 实例化:工厂Bean(FactoryBean)
在配置文件定义bean类型可以和返回类型不一样。
-
创建MyBean类,让这个类作为工厂 bean,实现接口 FactoryBean
public class MyBean implements FactoryBean<User> { //返回 Bean 的类型取决于 FactoryBean<T> 泛型值 @Override public User getObject() throws Exception { User user = new User(); return user; } }
-
第二步 xml配置
<bean id="myBean" class="com.tmsklst.factorypojo.MyBean"></bean>
-
第三步 测试
public void myBeanTest() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User myBean = context.getBean("myBean", User.class); System.out.println(myBean); }
Spring Bean的作用域
-
在 Spring 里面,设置创建 bean 实例是单实例还是多实例。
-
在 Spring 里面,默认情况下,bean 是单实例对象。
-
如何设置单实例还是多实例。
-
在 spring 配置文件 bean 标签里面有属性 scope 用于设置单实例还是多实例。
-
scope 属性:
-
默认值 singleton 表示是单实例对象。
-
prototype 表示是多实例对象。
<bean scope="singleton"></bean> <bean scope="prototype"></bean>
-
request 单实例对象。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。(不常用)
-
session 单实例对象。而对不同的 Session会话,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。(不常用)
-
-
-
singleton 和 prototype 区别
-
设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象。
<bean id="user1" class="com.tmsklst.pojo.User" scope="singleton"></bean> <bean id="user2" class="com.tmsklst.pojo.User" scope="propertype"></bean>
public void Test() { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User singUser01 = context.getBean("user1", User.class); User singUser02 = context.getBean("user1", User.class); System.out.println(singUser01);//com.tmsklst.pojo.User@2f8f5f62 System.out.println(singUser02);//com.tmsklst.pojo.User@2f8f5f62 User proUser01 = context.getBean("user2", User.class); User proUser02 = context.getBean("user2", User.class); System.out.println(proUser01);//com.tmsklst.pojo.User@5f4754f5 System.out.println(proUser02);//com.tmsklst.pojo.User@5f78f6f3 }
-
设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象。
-
Spring Bean的生命周期
-
创建Order类 ,并在类中自定以两个方法
initMethod()
与destroyMethod()
用于初始化和销毁。public class Order { private String name; public Order() { System.out.println("第一步 创建Bean的实例"); } public void setName(String name) { this.name = name; System.out.println("第二步 设置属性值"); } //初始化的方法 public void initMethod() { System.out.println("第三步 执行初始化方法"); } //销毁方法 public void destroyMethod() { System.out.println("第五步 执行销毁方法"); } }
-
创建MyBeanPost类 作为后置处理器并实现BeanPostProcessor接口。
public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前执行的方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之后执行的方法"); return bean; } }
-
创建xml配置文件,在bean标签中使用
init-method
与destroy-method
属性分别指定Order类中的初始化方法与销毁方法。<bean id="order" class="com.tmsklst.pojo.Order" init-method="initMethod" destroy-method="destroyMethod"> <property name="name" value="iPhont"></property> </bean> <!-- 配置后置处理器 --> <bean id="myBeanPost" class="com.tmsklst.pojo.MyBeanPost"></bean>
-
测试
public void test1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Object order = context.getBean("order", Order.class); System.out.println("第四步 获取创建bean实例对象"); System.out.println(order); //销毁实例 context.close(); }
Spring Bean自动注入
什么是自动注入?
根据指定装配规则属性名称或者属性类型,Spring自动将匹配的属性值进行注入。
Bean自动注入使用方法
给bean标签添加属性autowire:
-
byType:根据 xml 中 bean的类型自动注入。(对多种同源类型无效)
-
byName:根据 xml 中 bean的名称自动注入。
<bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byName"> <!-- 或 --> <bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byType">
案例:
Emp类:
public class Emp {
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void test() {
System.out.println(dept);
}
}
Dep类:
public class Dept {}
xml配置文件:
<bean id="emp" class="com.tmsklst.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.tmsklst.autowire.Dept"></bean>
测试:
public void test1() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Emp emp = context.getBean("emp", Emp.class);
System.out.println(emp);
}
Spring Bean自动注入druid连接池
-
在src下创建jdbc.properties配置文件
driverClassName=com.mysql.jdbc.Driver url=jdbc.mysql://localhost:3306/test username=root passowrd=root
-
在xml中新增名称空间context
<beans xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
名称空间引入配置文件
<context:property-placeholder location="classpath:jdbc.properties" />
-
bean配置
<bean id="druid" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${driverClassName}"></property> <property name="url" value="${url}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> </bean>
Spring Annotation (注解)
在 Spring 中,尽管使用 XML 配置文件可以实现 Bean 的装配工作,但如果应用中 Bean 的数量较多,会导致 XML 配置文件过于臃肿,从而给维护和升级带来一定的困难。使用注解大大简化了xml配置文件。
注解格式:@注解名称(属性名称=属性值,属性名称=属性值。。。)
可包含多个属性。
注解的使用范围:类、方法、属性。
所需依赖:spring-aop.jar
注解名称 | 描述 |
---|---|
@Component | 可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。 |
@Service | 通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Controller | 通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Repository | 用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。 |
@Autowired | 用于对 Bean 的属性变量、属性的 Set 方法及构造函数进行标注,配合对应的注解处理器完成 Bean 的自动配置工作。默认按照 Bean 的类型进行装配。 |
@Qualifier | 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。 |
@Resource | 其作用与 Autowired 一样。其区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。 @Resource 中有两个重要属性:name 和 type。 Spring 将 name 属性解析为 Bean 实例名称,type 属性解析为 Bean 实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean 类型进行装配。 如果都不指定,则先按 Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException 异常。 |
@Value | 给普通属性赋值。 |
注解注入Bean
配置注解扫描和注解扫描规则几种方式
方式一:指定包路径扫描
使用context命名空间,通知Spring扫描指定目录进行注解解析。(不同源的包用逗号隔开)
<context:component-scan base-package="com.tmsklst.annotation,
com.tmsklst1.dao,
com.tmsklst2.service" />
方式二:规则扫描定向注解
设置use-default-filters属性为false:不使用默认扫描规则,自定义配置规则。
通过include-filter标签指定注解:
-
type:annotation(注解)。
-
expression:注解包全路径。
<context:component-scan base-package="com.tmsklst" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
方式三:规则扫描反向注解(过滤注解)
通过exclude-filter标签指定需要无视的注解:
-
type:annotation(注解)。
-
expression:注解包全路径。(如果指定为Component,则该包下的Component注解不解析)
<context:component-scan base-package="com.tmsklst"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
注解Bean测试
-
使用context命名空间,通知Spring扫描指定目录进行注解解析。
<context:component-scan base-package="com.tmsklst" use-default-filters="false" > <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> </context:component-scan>
-
创建UserService类,在类上方使用注解。注解里的value值默认为类的名称,且首字母小写。
注解等同于:<bean id="userService" class="com.tmsklst.service.UserService" />
@Component(value = "userService") public class UserService { public void add() { System.out.println("service add……"); } }
-
测试
public void test1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
注解属性注入
@Autowired根据类型来注入属性值 (service和dao为案例)
-
Service层:创建UserService类, 属性上的 @Autowired注解会更具类型(UserDao)去Dao层找到相应的类。
@Service public class UserService { @Autowired private UserDao userDao; public void add() { System.out.println("service add……"); userDao.add(); } }
-
Dao层:创建UserDao实现类,别忘了给实现类装配bean(@Repository)
@Repository public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("UserDao 实现类一 add…………"); } }
@Qualifier根据Bean名称注入属性,与@Autowired 注解配合使用(service和dao为案例)
-
Service层:创建UserService类, 属性上的 @Autowired注解会更具类型(UserDao)去Dao层找到相应的类,当UserDao有多个实现类时,则加上@Qualifier注解,而value值是目标Bean名称。
@Service public class UserService { @Autowired @Qualifier(value = "userDao2") private UserDao userDao; public void add() { System.out.println("service add……"); userDao.add(); } }
-
Dao层:创建UserDao实现类,多个实现类需要定义value值(@Repository)
// 实现类一 @Repository(value = "userDaol") public class UserDaoImpl implements UserDao { @Override public void add() { System.out.println("UserDao 实现类一 add…………"); } } // 实现类二 @Repository(value = "userDao2") public class UserDaoImpl2 implements UserDao { @Override public void add() { System.out.println("UserDao 实现类二 add()…………"); } }
-
测试
public void test1() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); } /** com.tmsklst.service.UserService@2cbb3d47 service add…… UserDao 实现类二 add()………… */
@Resource根据Bean名称注入属性,默认按照 Bean 实例名称进行装配
@Service
public class UserService {
@Resource(name = "userDao")
private UserDao userDao;
public void add() {
System.out.println("service add……");
userDao.add();
}
}
Spring 使用配置类代替配置文件
新建一个SpringConfig类,使用@Configuration标注当前类为配置类,@ComponentScan指向解析注解的包。
@Configuration
@ComponentScan(basePackages = {"com.tmsklst5"})
public class SpringConfig {
}
AOP 面向切面编程
- 利用AOP(Aspect Oriented Programming)可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
AOP JDK动态代理
- JDK 动态代理是通过 JDK 中的 java.lang.reflect.Proxy 类实现的。
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
:返回指定接口的代理类实例,该接口将方法调用分派给指定的调用处理程序。- ClassLoader loader 参数:类的加载器。
- Class<?>[] interfaces参数:增强方法所在类的实现接口,支持多个接口。
- InvocationHandler h参数:实现InvocationHandler接口创建代理对象,增强代码部分。
实例:
-
创建接口UserDao,在UserDao添加两个方法,如下所示。
public interface UserDao { int add(int num1,int num2); String update(String name); }
-
创建UserDao的实现类UserDaoimpl,如下所示。
public class UserDaoimpl implements UserDao { @Override public int add(int num1, int num2) { System.out.println("第二:我是add方法原有逻辑……"); return num1 + num2; } @Override public String update(String name) { System.out.println("第二:我是uodate方法原有逻辑……"); return name; } }
-
创建JDKProxy类实现JDK动态代理。
public class JDKProxy { public static void main(String[] args) { /* 1.获取需要扩展功能的接口 */ Class[] interfaces = {UserDao.class}; /* 2.创建接口实现类对象 */ UserDao userDaoimpl = new UserDaoimpl(); /* 3.使用Proxy类中的newProxyInstance方法动态代理。 参数:类的加载器、接口、实现了InvocationHandler接口的类对象,此处也可用匿名InvocationHandler接口。 返回新的实例。 */ UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDaoimpl)); /* 8.使用新的实例调用方法 add()与 update() */ int add = userDaoProxy.add(10, 10); System.out.println("第四:addResult:" + add); String update = userDaoProxy.update("托马斯·克里斯特"); System.out.println("第四:updateResult:" + update); } } /* 4.创建实现了InvocationHandler接口类 */ class UserDaoProxy implements InvocationHandler { /* 5.接收目标类对象 */ private Object obj; /* 6.创建有参构造器,用于接收目标类对象 */ public UserDaoProxy(Object obj) { this.obj = obj; } /* 7.重写invoke方法用于增强逻辑 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前处理 if ("add".equals(method.getName())) { System.out.println("第一:我是add方法之前执行…… " + method.getName() + ":传递参数" + Arrays.asList(args)); } if ("update".equals(method.getName())) { System.out.println("第一:我是update方法之前执行…… " + method.getName() + ":传递参数" + Arrays.asList(args)); } /* 9.调用invoke方法被增强的方法执行,参数:目标类对象、参数 */ Object invoke = method.invoke(obj, args); //方法之后处理 System.out.println("第三:方法之后执行……" + obj); return invoke; } }
AspectJ开发AOP
AspectJ 是一个基于 Java 语言的 AOP 框架,它扩展了 Java 语言。Spring 2.0 以后,新增了对 AspectJ 方式的支持,新版本的 Spring 框架,建议使用 AspectJ 方式开发 AOP。
AspectJ 开发AOP的两种方式:
-
基于XML的声明方式。
-
基于Annotation的声明方式。
-
除了Spring Aop的jar包以外,还要导入与 AspectJ 相关jar包:
spring-aspects-5.2.6.RELEASE:
Spring 为 AspectJ 提供的实现。com.springsource.net.sf.cglib-2.2.0
com.springsource.org.aopalliance-1.0.0
com.springsource.org.aspectj.weaver-1.6.8.RELEASE:
AspectJ 提供的规范。
AspectJ 注解操作
AspectJ 框架为 AOP 开发提供了另一种开发方式——基于 Annotation 的声明式。AspectJ 允许使用注解定义切面、切入点和增强处理,而 Spring 框架则可以识别并根据这些注解生成 AOP 代理。
AspectJ 注解介绍
名称 | 描述 |
---|---|
@Aspect | 定义一个切面。 |
@Before | 定义前置通知。(在被增强的方法之前执行) |
@AfterReturning | 定义后置通知。(在程序正常执行后执行) |
@AfterThrowing | 定义异常通知。(有异常时执行) |
@After | 定义最终通知。(不管是否异常,都执行) |
@Around | 定义环绕通知。(在被增强的方法前后执行)被此注解所定义的方法必须携带ProceedingJoinPoint类型的参数,通过ProceedingJoinPoint调用proceed()方法,执行被增强的方法。 |
@DeclareParents | 用于定义引介通知。 |
@Order | 在有多个切面的情况下,设置优先级。数值越小,优先级越高。 |
@Pointcut | 对相同切入点作抽取。(对切入点表达式做封装)在其他切入点value值中调用被此注解标示的方法。 |
AspectJ 注解切入点表达式介绍
- 语法结构:@Before(value = “execution(【权限修饰符】【返回值类型】【目标类全路径】【方法名(【参数列表】)】)”)。
- *通配符:所有的。
- 没有返回值类型用空格代替。
- 实例1:对 aop.tmsklst.aspectJannotation.User 类的add方法增强。
execution(* aop.tmsklst.aspectJannotation.User.add(..))
- 实例2:对 aop.tmsklst.aspectJannotation.User 类的所有方法增强。
execution(* aop.tmsklst.aspectJannotation.User.*(..))
- 实例3:对 aop.tmsklst.aspectJannotation 包下的所有方法增强。
execution(* aop.tmsklst.aspectJannotation.*.*(..))
AspectJ 注解的使用
-
在配置文件中使用context名称空间启用Annotation扫描,在使用aop名称空间开启AspectJ切面。
<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- Annotation扫描 --> <context:component-scan base-package="aop.tmsklst"></context:component-scan> <!-- 开启AspectJ生成切面 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
或 使用配置类替换配置文件。
@ComponentScan @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
-
被增强类
@Component public class User { public void add() { // int i = 1/0; //制造异常 System.out.println("add......"); } }
-
创建切面类,使用@Aspect注解表示当前类是切面类,用@Order设置优先级。
@Aspect //定义一个切面 @Order(1)//在有多个切面的情况下,设置优先级 @Component public class UserProxy { //相同切入点抽取 @Pointcut(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..) )") public void pointcut() { } //@Before 注解表示作为前置通知 @Before(value = "pointcut())") public void defore() { System.out.println("defore...."); } //最终通知:不管在扫描情况下都执行 @After(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))") public void after() { System.out.println("after...."); } //后置通知:在程序正常执行后执行 @AfterReturning(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))") public void afterReturning() { System.out.println("afterReturning...."); } //异常通知:有异常时执行 @AfterThrowing(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))") public void afterThrowing() { System.out.println("afterThrowing...."); } //环绕通知 @Around(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("环绕之前...."); //被增强的方法执行 proceedingJoinPoint.proceed(); System.out.println("环绕之后...."); } }
-
创建第二个切面类,使用@Aspect注解表示当前类是切面类,用@Order设置优先级。
@Aspect //定义一个切面 @Order(2)//在有多个切面的情况下,设置优先级 @Component public class PersonProxy { @Before(value = "execution(* aop.tmsklst.aspectJannotation.User.add(..))") public void before() { System.out.println("PersonProx before...."); } }
-
测试
public void testAno1() { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); User user = context.getBean("user", User.class); user.add(); } /* 执行结果 异常结果 环绕之前.... 环绕之前.... defore.... defore.... PersonProx before.... PersonProx before.... add...... after.... 环绕之后.... afterThrowing.... after.... afterReturning.... */
AspectJ 配置文件操作
-
被增强类
public class Person { public void add() { System.out.println("add...."); } }
-
切面类
public class PersonProxy { public void before() { System.out.println("before...."); } }
-
在配置文件中添加aop名称空间。
<beans xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 配置AOP增强 --> <aop:config> <!-- 切入点 --> <aop:pointcut id="p" expression="execution(* aop.tuoyingtao.aspectJxml.Person.add(..))"></aop:pointcut> <!-- 切面 --> <aop:aspect ref="personProxy"> <!-- 增强方法 --> <aop:before method="before" pointcut-ref="p"></aop:before> </aop:aspect> </aop:config>
Spring JDBCTemplate操作数据库
什么是JDBCTemplate?
它是Spring框架对JDBC的封装,使用JDBCTemplate更方便实现对数据库操作。
导入相关jar包
- druid-1.1.9:链接池
- mysql-connector-java-5.1.7-bin:数据库连接
- spring-tx-5.2.6.RELEASE:事务处理
- spring-jdbc-5.2.6.RELEASE:spring对jdbc的封装
JDBCTemplate使用实例
-
jdbc.properties 配置数据库连接基本信息。
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/user_db username=root passwprd=root
-
Spring 配置文件添加context名称空间,引入jdbc.properties配置文件,启用注解解析,创建JdbcTemplate对象,并注入链接池。
<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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="jdbc.properties" /> <context:component-scan base-package="aop2.jdbctemplate"/> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${driverClassName}" /> <property name="url" value="${url}" /> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <!-- JdbcTemplate对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 注入链接池 --> <property name="dataSource" ref="druidDataSource"/> </bean> </beans>
-
BookDaoImpl类实现BookDao接口,并注入JdbcTemplate对象。
@Repository public class BookDaoImpl implements BookDao { //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; }
-
在BookService类中注入BookDaoImpl类。
@Service public class BookService { //注入dao @Autowired private BookDao bookDao; }
JDBCTemplate 操作数据库(增、删、改)
-
创建Book类
public class Book { private Integer id; private String userName; private String status; ………… }
-
service层
@Service public class BookService { //注入dao @Autowired private BookDao bookDao; // 添加数据 public void addBook(Book book) { bookDao.add(book); } //修改数据 public void amendBook(Book book) { bookDao.amend(book); } //删除数据 public void deleteBook(Integer id) { bookDao.delete(id); } }
-
dao层
@Repository public class BookDaoImpl implements BookDao { //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; //插入数据 @Override public void add(Book book) { String sql = "insert into t_book(id,userName,status) values(?,?,?)"; Object[] args = {book.getId(),book.getUserName(),book.getStatus()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } //修改数据 @Override public void amend(Book book) { String sql = "update t_book set userName = ?, status = ? where id = ?"; Object[] args = {book.getUserName(),book.getStatus(),book.getId()}; int update = jdbcTemplate.update(sql, args); System.out.println(update); } //删除数据 @Override public void delete(Integer id) { String sql = "delete from t_book where id = ?"; int update = jdbcTemplate.update(sql, id); System.out.println(update); } }
-
测试
//插入数据 public void jdbcTemplateTest() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BookService bookService = context.getBean("bookService", BookService.class); bookService.addBook(new Book(null,"托马斯·克里斯特","true")); } //修改数据 public void jdbcTemplateTest2() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BookService bookService = context.getBean("bookService", BookService.class); bookService.amendBook(new Book(1,"Tom","false")); } //删除数据 public void jdbcTemplateTest3() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); BookService bookService = context.getBean("bookService", BookService.class); bookService.deleteBook(1); }
JDBCTemplate 操作数据库(查询、批量操作)
-
service层
@Service public class BookService { //注入dao @Autowired private BookDao bookDao; public Integer findCount() { return bookDao.countTotal(); } public Book findBook(Integer id) { return bookDao.findBookInfo(id); } public List<Book> findAll() { return bookDao.findAllList(); } public void batcAdd(List<Object[]> batchArgs) { bookDao.batchAddBook(batchArgs); } }
-
dao层
@Repository public class BookDaoImpl implements BookDao { //注入JDBCTemplate @Autowired private JdbcTemplate jdbcTemplate; //总共多少条记录 @Override public Integer countTotal() { String sql = "select count(*) from t_book"; Integer integer = jdbcTemplate.queryForObject(sql, Integer.class); return integer; } //查询详情 @Override public Book findBookInfo(Integer id) { String sql = "select id, userName, status from t_book where id = ?"; Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id); return book; } //查询列表 @Override public List<Book> findAllList() { String sql = "select id,userName,status from t_book"; List<Book> query = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class)); return query; } //批量添加(删除、修改类似) @Override public void batchAddBook(List<Object[]> batchArgs) { String sql = "insert into t_book(id,userName,status) values(?,?,?)"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); } }
-
测试
@Test public void jdbcTemplateTest4() { Integer count = bookService.findCount(); System.out.println(count); } @Test public void jdbcTemplateTest6() { Book book = bookService.findBook(2); System.out.println(book); } @Test public void jdbcTemplateTest7() { List<Book> all = bookService.findAll(); Iterator<Book> iterator = all.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } } @Test public void jdbcTemplateTest8() { List<Object[]> list = new ArrayList<>(); Object[] o1 = {null,"name11","true"}; Object[] o2 = {null,"name22","false"}; Object[] o3 = {null,"name33","true"}; list.add(o1); list.add(o2); list.add(o3); bookService.batcAdd(list); }
Spring 事务操作
事务是逻辑上的一组操作,要么都执行,要么都不执行。
事务的特性(ACID)
原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
Spring事务管理API
PlatformTransactionManager 接口介绍
PlatformTransactionManager接口表示事务管理器,为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,更具不同的框架提供不同的实现类。
PlatformTransactionManager 接口中定义的三个方法:
public interface PlatformTransactionManager extends TransactionManager {
//根据指定的传播行为,返回当前活动的事务或创建一个新事务。
TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
//提交事务
void commit(TransactionStatus var1) throws TransactionException;
//对执行的事务进行回滚
void rollback(TransactionStatus var1) throws TransactionException;
}
PlatformTransactionManager 接口对应不同框架的实现类
事务 | 描述 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManager | 使用Spring JDBC或者MyBatis进行持久化数据时使用。 |
org.springframework.orm.hibernate5.HibernateTransactionManager | 使用Hibernate进行数据持久化时使用。 |
org.springframework.orm.jpa.JpaTransactionManager | 使用JPA进行数据持久化时使用。 |
org.springframework.transaction.jta.JtaTransactionManager | 使用JTA实现管理事务,在一个事务跨越多个资源时使用。 |
当我们使用JDBC或Mybatis进行数据持久化操作时,xml配置操作如下:
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="DruidDataSource"/>
</bean>
TransactionDefinition 接口介绍
事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition var1)
方法来得到一个事务,这个方法里面的参数是 TransactionDefinition类 ,这个类就定义了一些基本的事务属性。
@Transactional 注解
@Transactional 注解定义在类上表示当前类中所有方法都添加事务。若在方法上,则表示为此方法添加事务。
propagation 事务传播行为:多事务方法直接进行调用,这个过程中事务是如何进行管理的。
ioslation 事务隔离级别:事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题(脏读、不可重复读、幻读)
隔离级别 | 描述 |
---|---|
READ UNCOMMITTED (读未提交数据) | 允许事务读取未被其它事务提交更改。脏读、不可重复读以及幻读的问题都会出现。 |
READ COMMITED (读已提交数据) | 只允许事务读取已经被其它事务提交的变更,可以避免脏读。但不可重复读和幻读问题仍然可能出现。 |
REPEATABLE READ (可重复读) | 确保事务可以多次从一个字段中读取相同的值。 在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读。但幻读的问题仍然存在。 |
SERIALIZABLE (串行化) | 确保事务可以从一个表中读取相同的值。 在这个事务持续期间。禁止其他事务对该表执行插入、更新、删除操作,所有并发问题都可以避免,但性能十分低下。 |
timeout 超时时间:事务需要在一定时间内进行提交,如果不提交进行回滚。默认值是 -1(时间以秒单位计算)
readOnly 是否只读:读:查询操作,写:添加修改删除操作。默认值 false。
rollbackFor 回滚:设置出现哪些异常进行事务回滚。
noRollbackFor 不回滚:设置出现哪些异常不进行事务回滚。
案例:
-
注解配置中添加tx命名空间
<beans xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 事务管理器 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="DruidDataSource"/> </bean> <!-- 开启事务注解 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
或 使用xml配置
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref="DruidDataSource"/> </bean> <!-- xml配置通知 --> <tx:advice id="txadvice"> <!-- 配置事务参数 --> <tx:attributes> <tx:method name="account*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切入点和切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut id="pt" expression="execution(* spring.tm.service.UserService.*(..))"/> <!-- 配置切面 --> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
-
service层(如果使用xml配置就需要添加@Transactional注解)
@Service @Transactional(readOnly = false,timeout = 1,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) public class UserService { @Autowired private UserDao userDaoImpl; public void accountMoney(Integer addId,Integer reduceId, BigDecimal money) { userDaoImpl.addMoney(addId,money); //模拟异常 int i = 10/0; userDaoImpl.reduceMoney(reduceId,money); } }
Spring使用配置类代替xml配置文件
@Configuration//配置类
@ComponentScan(basePackages = "spring.tm")//组件扫描
@EnableTransactionManagement //开启事务
public class AnnotationConfig {
//创建数据库链接池
@Bean
public DataSource getDruidDataSource() {
try {
Properties properties = new Properties();
InputStream resourceAsStream =
ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
properties.load(resourceAsStream);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
return dataSource;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 创建JdbcTemplate对象
* @param dataSource 在IOC容器中更具类型找到DataSource对象
* @return
*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
/**
* 创建事务管理器
* @param dataSource 在IOC容器中更具类型找到DataSource对象
* @return
*/
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
Spring5 框架新功能
整个 Spring5 框架的代码基于 Java8,运行时兼容 JDK9,许多不建议使用的类和方法在代码库中已删除。
Spring5 框架日志
Spring5 已经移除 Log4jConfigListener,官方建议使用 Log4j2。
1. 导入jar包
2. 创建log4j2.xml配置文件(名字是固定的)
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
Spring5 框架核心容器支持@Nullable 注解
@Nullable 注解可以使用在方法上面,属性上面,参数上面,表示方法返回可以为空,属性值可以 为空,参数值可以为空
注解用在方法上面,方法返回值可以为空。
@Nullable
String getId()
注解使用在方法参数里面,方法参数可以为空。
String getId(@Nullable Integer id)
注解使用在属性上面,属性值可以为空。
@Nullable
private String id;
Spring5 核心容器支持函数式风格 GenericApplicationContext
函数式风格创建对象,并交给Spring 进行管理。
public void genericApplicationContextTest() {
//1.创建GenericApplicationContext对象
GenericApplicationContext genericApplicationContext = new GenericApplicationContext();
//2.调用context的方法对象注册
genericApplicationContext.refresh();
genericApplicationContext.registerBean(User.class,() -> new User());
genericApplicationContext.registerBean("myUser",User.class,() -> new User());
//3.获取在spring注册的对象
User user = (User) genericApplicationContext.getBean("spring.tm.pojo.User");
User user2 = (User) genericApplicationContext.getBean("myUser");
System.out.println(user);
System.out.println(user2);
}
Spring5 支持整合 JUnit5
整合 JUnit4
@RunWith(SpringJUnit4ClassRunner.class)//单元测试框架
@ContextConfiguration("classpath:ApplicationContext2.xml")//加载配置文件
public class JTest4 {
@Autowired
private UserService userService;
@Test
public void test() {
userService.accountMoney(1001,1002,new BigDecimal(200));
}
}
整合 JUnit5
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath*:ApplicationContext2.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test() {
userService.accountMoney(1001,1002,new BigDecimal(300));
}
}
使用复合注解替代上面两个注解完成整合。
@SpringJUnitConfig(locations = "classpath*:spring/tm/ApplicationContext2.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test() {
userService.accountMoney(1001,1002,new BigDecimal(300));
}
}