Spring5
Spring5
1.Spring
1.1、Spring简介
-
Srping是一个轻量级的控制反转(IoC) 和面向切面(AOP) 的容器框架
-
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版本
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--Spring和MyBatis整合需要的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
官网:https://spring.io/projects/spring-framework#overview
下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
1.2、优点
- 开源免费
- 轻量级、非侵入式
- 控制反转(IOC)、面向切面(AOP)
- 支持事务的处理、支持其它框架整合(大杂烩)
1.3、组成
2、IOC(控制反转)
将对象的控制权交给用户,业务层不再自己new对象。为用户提供setter器,被动等待接受用户new的对象
3、HelloSpring
3.1、配置元数据基本结构
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- dao层bean的配置 -->
<bean id="studentDao" class="com.zmy.dao.StudentDao">
</bean>
<bean id="teacherDao" class="com.zmy.dao.TeacherDao">
</bean>
<!-- service层bean的配置 -->
<bean id="personService" class="com.zmy.service.PersonService">
<!--value:基本类型+String ref:复杂类类型-->
<property name="personDao" ref="teacherDao"/>
</bean>
</beans>
3.2、Dao层
public interface PersonDao {
void doSomething();
}
public class StudentDao implements PersonDao{
@Override
public void doSomething() {
System.out.println("学生要学习");
}
}
public class TeacherDao implements PersonDao{
@Override
public void doSomething() {
System.out.println("老师要教学");
}
}
3.3、获取bean并使用
@Test
public void testHello(){
//拿到容器上下文对象
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonService personService = (PersonService) classPathXmlApplicationContext.getBean("personService");
personService.serviceOne();
}
4、Spring配置说明
4.1、alias(别名)
<beans>
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg name="name" value="18岁的我"/>
<constructor-arg name="age" value="18"/>
</bean>
<alias name="student" alias="ppp"/>
</beans>
测试
public void testHello(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) applicationContext.getBean("ppp");
System.out.println(student.toString());
}
4.2、Bean
<!--
id:名字
class:类型全限定名 包名+类名
name:别名 可取多个 ',' ' ' ';'都可分割
scope:作用域(不指定时,默认单例) singleton=单例 prototype=原型(每次get会新建一个实例)
-->
<bean id="student" class="com.zmy.pojo.Student" name="student2,student3 student4;student5" scope="singleton">
<constructor-arg type="java.lang.String" value="名字第一个"/>
<constructor-arg type="int" value="1"/>
<constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>
4.3、Import
合并多个xml
<import resource="beans.xml">
5、依赖注入(DI)
5.1、构造器注入
5.1.1、使用无参构造函数构造
默认使用无参构造函数构造,此时需要保证有无参构造函数,否则报错
public class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
<bean id="student" class="com.zmy.pojo.Student">
<!--property设置的值通过set方法赋值-->
<property name="name" value="学生set名字"/>
<property name="age" value="18"/>
</bean>
5.1.2、使用有参构造函数构造
5.1.2.1、通过下标传参
student类,没有无参构造函数
package com.zmy.pojo;
public class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg index="0" value="学生构造器名字"/>
<constructor-arg index="1" value="0"/>
</bean>
5.1.2.2、通过类型传参
student类
package com.zmy.pojo;
public class Student{
private String name1;
private String name2;
private int age;
public Student(String name1, String name2,int age) {
this.name1 = name1;
this.name2 = name2;
this.age = age;
}
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name1='" + name1 + '\'' +
"name2='" + name2 + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<!--每种类型根据前后顺序为第一个String类型参数,第二个String类型参数...-->
<!--第一个出现的String类型,为参数列表中的第一个String类型参数的值-->
<constructor-arg type="java.lang.String" value="名字第一个"/>
<constructor-arg type="int" value="1"/>
<!--第二个出现的String类型,为参数列表中的第二个String类型参数的值-->
<constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>
5.1.2.3、通过参数名传参
student类
package com.zmy.pojo;
public class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg name="name" value="18岁的我"/>
<constructor-arg name="age" value="18"/>
</bean>
总结:在配置文件加载的时候,所有配置的bean都会被创建唯一一个对象。每次get对象都是同一个实例。
5.2、Set方式注入
实体类MyClass
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class MyClass {
private int id;
private String name;
private Properties props;
Teacher teacher;
List<Student> students;
Map<String,Teacher> mapTeacher;
Set<Teacher> teachers;
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public Map<String, Teacher> getMapTeacher() {
return mapTeacher;
}
public void setMapTeacher(Map<String, Teacher> mapTeacher) {
this.mapTeacher = mapTeacher;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", name='" + name + '\'' +
", props=" + props +
", teacher=" + teacher +
", students=" + students +
", mapTeacher=" + mapTeacher +
", teachers=" + teachers +
'}';
}
}
实体类Student
public class Student{
private String name;
private int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name1='" + name + '\'' +
", age=" + age +
'}';
}
}
实体类Teacher
public class Teacher{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
基本类型+String
引用的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- pojo层bean的配置 -->
<bean id="student" class="com.zmy.pojo.Student" scope="prototype">
<property name="name" value="XXXX"/>
<property name="age" value="18"/>
</bean>
<bean id="teacher" class="com.zmy.pojo.Teacher" scope="prototype">
<property name="name" value="XXXX"/>
<property name="age" value="53"/>
</bean>
<bean id="myClass" class="com.zmy.pojo.MyClass">
<property name="name" value="XXX"/>
<property name="id" value="13"/>
<property name="teacher" ref="teacher"/>
<property name="props">
<props>
<prop key="p1">v1</prop>
<prop key="p2">v2</prop>
</props>
</property>
<property name="students">
<list>
<ref bean="student"/>
<ref bean="student"/>
<ref bean="student"/>
</list>
</property>
<property name="mapTeacher">
<map>
<entry key="teacher1" value-ref="teacher"/>
</map>
</property>
<property name="teachers">
<set>
<ref bean="teacher"/>
</set>
</property>
</bean>
</beans>
输出结果
MyClass{id=13, name='XXX', props={p2=v2, p1=v1}, teacher=Teacher{name='XXXX', age=53}, students=[Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}], mapTeacher={teacher1=Teacher{name='XXXX', age=53}}, teachers=[Teacher{name='XXXX', age=53}]}
false
5.3、扩展方式注入
使用p标签或者c标签
p标签=properties c标签=constructor-arg
使用p或者c标签需要导入对应的命名空间
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
<!--需要有无参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" p:name="XXX" p:age="53">
<!--需要有有参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" c:name="XXX" c:age="53">
6、Bean的作用域
scope
- singleton 单例,每次get都是同一个实例,默认为单例
- prototype 原型,每次get都会新创建一个实例
- request
- session
- application
- websocket
7、Bean的自动装配
Spring会在上下文中自动寻找bean,自动给bean装配属性
7.1、byName自动装配
<bean id="cat" class="Cat"/>
<bean id="dog" class="Dog"/>
<!--手动set注入装配-->
<bean id="people" class="People">
<property name="name" value="XX">
<property name="cat" ref="cat">
<property name="dog" ref="dog">
</bean>
<!--自动装配-->
<bean id="people" class="People" autowire="byName">
<property name="name" value="XX">
</bean>
byName会自动在容器上下文中寻找set方法指定的beanid
eg:public void setCat(){…} 会寻找id为Cat的bean
7.2、byType自动装配
每个类的bean只能有一个
<!--自动装配-->
<bean id="people" class="People" autowire="byType">
<property name="name" value="XX">
</bean>
7.3、注解实现自动装配
-
Spring从2.5开始支持注解,JDK从1.5开始支持注解
-
使用注解的前提条件
1、导入约束 (context约束)
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
2、开启注解支持
<context:annotation-config/>
3、@Autowired和@Qualifier
在需要自动装配的属性上 或者 set方法上添加**@Autowired**注解,自动寻找xml中的bean(根据类型而非id)
也可以增加@Qualifier(“dog”)注解,则会根据id去寻找对应的bean
4、@Resource(name=“XXX”)
等同于@Autowired + @Qualifier
- 示例
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<!-- pojo层bean的配置 -->
<bean id="cat" class="com.zmy.pojo.Cat" />
<bean id="dog" class="com.zmy.pojo.Dog" />
<bean id="person" class="com.zmy.pojo.Person">
<property name="name" value="XXX"/>
</bean>
</beans>
package com.zmy.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
private String name;
@Autowired
//寻找类型为Cat的bean
private Cat cat;
@Autowired
//寻找id为dog的bean
@Qualifier("dog")
private Dog dog;
public Person() {
}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
8、使用注解开发
使用注解开发,需要增加context约束,开启注解扫描并
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解,并扫描指定包下的注解,可实现注册Bean-->
<context:component-scan base-package="com.zmy.pojo"/>
</beans>
8.1、注册Bean
使用注解 @Component(value = “XXX”) 进行注册Bean,XXX为Bean的id
8.2、属性的注入
使用注解 @Value(“zmy”) 实现简单属性的注入
package com.zmy.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Person {
@Value("zmy")
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public Person() {
}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
package com.zmy.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dog {
public void shout(){
System.out.println("wang");
}
}
package com.zmy.pojo;
import org.springframework.stereotype.Component;
@Component
public class Cat {
public void shout(){
System.out.println("miao");
}
}
8.3、@Component的衍生注解
在web项目中,由于mvc三层架构,不同层会使用不同的注解注册Bean,功能一致
-
dao层
@Repository
-
service
@Service
-
controller
@Controller
8.4、作用域
@Scope(“singleton”)
9、使用JavaConfig代替xml注册Bean
定义JavaConfig类来代替xml配置文件,JavaConfig类可以做所有xml能够做的配置
- 增加 @Configuration 注解
- 增加 @Bean 注解
package com.zmy.javaconfig;
import com.zmy.pojo.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
//配置扫描包
@ComponentScan(value = "com.zmy.pojo")
//导入另外一个配置类
@Import(JavaConfig2.class)
public class JavaConfig {
@Bean
public Dog dog(){
return new Dog();
}
}
@Test
public void testHello(){
ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
Dog dog = annotationConfigApplicationContext.getBean("dog", Dog.class);
dog.shout();
}
10、AOP(面向切面)
AOP底层是使用代理模式实现的,所以首先要了解代理模式。
代理模式分为:
- 静态代理
- 动态代理
10.1、静态代理
eg:结婚案例。
- 结婚对象 : 实现 结婚接口,实现结婚方法
- 婚庆公司 : 实现结婚接口,拥有结婚对象,结婚方法中调用结婚对象的结婚方法,并且有一些自己的操作
- 结婚接口 :结婚方法
静态代理的好处:
- 使真实对象只需关心核心业务
- 公共业务交给代理去做
静态代理的缺点:
- 每一个真实对象都需要一个代理,代码量翻倍。
10.2、动态代理
学习动态代理之前,先了解两个java.lang.reflect包下的类
-
Proxy 类
- public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException 该方法返回一个interfaces接口的代理类对象
-
InvocationHandler 接口
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
实现了该接口的类的对象,作为参数传递给newProxyInstance方法
-
获取代理工具类
package com.zmy.pojo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的真实对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//通过Proxy.newProxyInstance,返回target的代理类对象 : $Proxy0
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//1、$Proxy0的代码由JVM生成
//2、$Proxy0有一个私有属性,InvocationHandler h,在Proxy.newProxyInstance的第三个参数,(我们传入的this,即当前类的对象)
// $Proxy0类对象在调用接口方法时,内部实现仅调用了h的invoke方法,所以h必须有方法invoke,而我们传入的this当前类对象的类实现了InvocationHandler接口,保证了该点
// invoke调用时传入的参数为参数为:this, 接口方法, null
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//调用被代理的真实对象的方法method
Object object = method.invoke(target,args);
after();
return object;
}
private void after() {
System.out.println("结婚后的收尾!");
}
private void before() {
System.out.println("结婚前的准备!");
}
}
Marry接口
public interface Marry {
void HappyMarry();
}
You类
public class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚了!");
}
}
测试类
@Test
public void test(){
You you = new You();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(you);
Marry proxy = (Marry) proxyInvocationHandler.getProxy();
proxy.HappyMarry();
}
参考自:https://www.jb51.net/article/129142.htm
推荐阅读
-
spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)
-
Spring5源码解析5-ConfigurationClassPostProcessor (上)
-
spring5 源码深度解析----- AOP代理的生成
-
Spring5源码解析4-refresh方法之invokeBeanFactoryPostProcessors
-
spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
-
spring5 源码深度解析-----ApplicationContext容器refresh过程
-
Spring5源码解析6-ConfigurationClassParser 解析配置类
-
Activiti6.0 spring5 工作流引擎 java SSM流程审批 项目框架
-
Spring5学习之基础知识总结
-
【6】Spring5中Bean的自动装配