简介
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。
Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。
Spring使用
创建maven工程,在pom.xml中添加spring依赖
1 2 3 4 5
| <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.7.RELEASE</version> </dependency>
|
创建User类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| package org.example.learn;
import java.util.Date;
public class User { private String username; private int age; private String address; private Date date;
public User() { }
public User(String username, int age, String address, Date date) { this.username = username; this.age = age; this.address = address; this.date = date; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public Date getDate() { return date; }
@Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", address='" + address + '\'' + ", date=" + date + '}'; }
public void setDate(Date date) { this.date = date; } }
|
创建Spring配置文件applicationContext.xml,在配置文件中加入User bean
1 2 3 4 5 6 7
| <?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"> <bean id="user" class="org.example.learn.User"></bean> </beans>
|
test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package org.example;
import org.example.learn.User; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App { public static void main( String[] args ) { ClassPathXmlApplicationContext cl = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = (User) cl.getBean("user"); System.out.print(user); } }
|
基本属性注入
构造注入
平时都是在new的时候或者使用set方法对其进行赋值,在spring框架下,只需要配置xml文件就能进行构造器的注入
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?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"> <bean id="user" class="org.example.learn.User"> <constructor-arg name="username" value="zhangshan" /> <constructor-arg name="age" value="18" /> <constructor-arg name="address" value="Beijing" /> <constructor-arg name="date" ref="now" /> </bean>
<bean id="now" class="java.util.Date"></bean> </beans>
|
注:类中需要提供一个对应参数列表的构造函数
constructor-arg标签属性:
index:指定参数在构造函数参数列表的索引位置
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称
value:它能赋的值是基本数据类型和 String 类型
ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
Set注入
配置xml文件
1 2 3 4 5 6 7 8 9
| <bean id="user" class="org.example.learn.User"> <property name="username" value="lisi" /> <property name="age" value="18" /> <property name="address" value="Beijing" /> <property name="date" ref="now" /> </bean>
<bean id="now" class="java.util.Date"></bean>
|
property标签属性:
name:找的是类中 set 方法后面的部分
ref:给属性赋值是其他 bean 类型的
value:给属性赋值是基本数据类型和 string 类型的
实际开发中,此种方式用的较多。
Java配置
在Spring中,将一个Bean注册到Spring容器中,有三种不同的方式:
- xml注入(如第3节所述)
- Java配置(本节所述)
- 自动化扫描
下面有一个Bean:
1 2 3 4 5 6 7
| package org.example.learn;
public class SayHello { public String sayHello(String name) { return "hello" + name; } }
|
在Java中,使用一个Java配置类代替之前的XML配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package org.example.learn;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
@Configuration public class Javaconfig { @Bean SayHello sayHello() { return new SayHello(); } }
|
测试:在项目启动时,加载配置类,将Bean注册到Spring容器中
1 2 3 4 5 6
| @Test public void TestJavaConfig() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Javaconfig.class); SayHello hello = ctx.getBean(SayHello.class); System.out.print(hello.sayHello("mike")); }
|
隐式的bean发现机制和自动装配
例子
创建实体类
Cat类:
1 2 3 4 5 6 7
| package org.example.learn;
public class Cat { public void shout() { System.out.println("miaomiaomiao"); } }
|
Dog类:
1 2 3 4 5 6 7
| package org.example.learn;
public class Dog { public void shout() { System.out.println("wangwangwang"); } }
|
Person类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| package org.example.learn;
public class Person { private String username; private Cat cat; private Dog dog;
public Person(String username, Cat cat, Dog dog) { this.username = username; this.cat = cat; this.dog = dog; }
public Person() {
}
@Override public String toString() { return "Person{" + "username='" + username + '\'' + ", cat=" + cat + ", dog=" + dog + '}'; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
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; } }
|
在Spring配置文件上注入Bean
1 2 3 4 5 6 7 8
| <bean id="cat" class="org.example.learn.Cat" /> <bean id="dog" class="org.example.learn.Dog" />
<bean id="person" class="org.example.learn.Person"> <property name="username" value="mike" /> <property name="cat" ref="cat" /> <property name="dog" ref="dog" /> </bean>
|
测试:
1 2 3 4 5 6 7
| @Test public void TestPerson() { ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("applicationContext.xml"); Person person = (Person) cx.getBean("person"); person.getCat().shout(); person.getDog().shout(); }
|
ByName自动装配
修改Spring配置
1 2 3 4 5 6 7 8
| <bean id="cat" class="org.example.learn.Cat" /> <bean id="dog" class="org.example.learn.Dog" />
<bean id="person" class="org.example.learn.Person" autowire="byName"> <property name="username" value="mike" /> </bean>
|
byName的执行过程:
- byName依赖于实体类的set方法
- 查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常(java.lang.NullPointerException)。
ByType自动装配
修改Spring Bean注入配置文件
1 2 3 4 5 6 7 8
| <bean class="org.example.learn.Cat" /> <bean class="org.example.learn.Dog" />
<bean id="person" class="org.example.learn.Person" autowire="byType"> <property name="username" value="mike" /> </bean>
|
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
1
| NoUniqueBeanDefinitionException
|
byType注意:
- ByType按照类型自动装配
- 每个bean的类型必须唯一,不可重复
- 它不依赖set方法,而是按照对应的类型进行装配
总结:
- 使用byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
- 使用byType的时候,需要保证所有bean的Class唯一,并且这个bean需要和自动注入的属性的类型一致!
使用注解实现自动装配
jdk1.5开始支持注解,spring2.5开始全面支持注解
准备:
导入约束:context约束
配置注解的支持
1
| <context:annotation-config/>
|
Spring 配置文件:
1 2 3 4 5 6 7 8 9 10 11 12
| <?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/>
</beans>
|
@Autowired
在实体类的属性上添加@Autowired注解
1 2 3 4
| @Autowired private Cat cat; @Autowired private Dog dog;
|
修改Spring配置文件
1 2 3 4 5 6
| <context:annotation-config/>
<bean id="cat1" class="org.example.learn.Cat" /> <bean id="dog1" class="org.example.learn.Dog" />
<bean id="newperson" class="org.example.learn.NewPerson" />
|
注:@Autowired 注释,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作
@Autowired是按类型自动转配的,不支持id匹配。
直接在属性上添加@Autowired注解即可,也可以添加在setter方法上。
使用了@Autowired的属性,不需要setter方法也可以实现注入
@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。
1 2 3
| @Autowired(required = false) private Cat cat;
|
@Nullable,可以用于方法,属性,表示可以为null
1 2 3 4
| public People(@Nullable String name) { this.name = name; }
|
@Qualifier
修改Bean,一个类有多个对象
1 2 3 4
| <bean id="cat1" class="org.example.learn.Cat" /> <bean id="cat2" class="org.example.learn.Cat" /> <bean id="dog1" class="org.example.learn.Dog" /> <bean id="dog2" class="org.example.learn.Dog" />
|
在实体类中需要加上@Qualifier
1 2 3 4 5 6
| @Autowired @Qualifier("cat1") private Cat cat; @Autowired @Qualifier("dog1") private Dog dog;
|
@Resource
修改实体类
1 2 3 4
| @Resource(name="cat1") private Cat cat; @Resource private Dog dog;
|
修改配置文件
1 2 3 4 5 6
| <context:annotation-config/>
<bean id="cat1" class="org.example.learn.Cat" /> <bean id="cat2" class="org.example.learn.Cat" /> <bean id="dog" class="org.example.learn.Dog" /> <bean id="dog2" class="org.example.learn.Dog" />
|
注:@Resource不是spring的注解,属于 javax.annotation.Resource ;它也可以实现自动装配
- @Resource可以指定的name属性,设置name后使用指定的name进行byName方式查找装配;
- 上面的方法没找到再使用默认的byName方式查找(方法名小写)
- 通过byName方法没有找到时,再使用byType的方式装配
- 如果byName和byType方法全部没有找到,则会报错!
AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
方法一
添加依赖
1 2 3 4 5
| <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency>
|
编写业务接口和实现类
1 2 3 4 5 6 7 8
| package org.example.learn;
public interface service1 { void add(); void delete(); void update(); void query(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package org.example.learn;
public class UserService implements service1 { @Override public void add() { System.out.println("添加方法"); }
@Override public void query() { System.out.println("查询方法"); }
@Override public void delete() { System.out.println("删除方法"); }
@Override public void update() { System.out.println("更新方法"); } }
|
增强类:前置增强和后置增强
1 2 3 4 5 6 7 8 9 10 11 12
| package org.example.learn;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class log implements MethodBeforeAdvice { public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass().getName()+"De"+method.getName()+"方法执行了"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| package org.example.learn;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class Afterlog implements AfterReturningAdvice { public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable { System.out.println("执行了"+method.getName()+"执行结果为"+o); } }
|
在spring配置文件aopContext.xml中注册,实现AOP切入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="org.example.learn.UserService"/> <bean id="log" class="org.example.learn.log"/> <bean id="afterlog" class="org.example.learn.Afterlog"/>
<aop:config> <aop:pointcut id="pointcut" expression="execution(* org.example.learn.UserService.*(..))"/> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/> </aop:config>
</beans>
|
测试
1 2 3 4 5 6
| @Test public void TestService() { ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("aopContext.xml"); service1 service = (service1) cx.getBean("userservice"); service.delete(); }
|
方法二
业务接口和实现类同上
使用自定义的切入类DiyPointCut.class
1 2 3 4 5 6 7 8 9 10
| package org.example.learn;
public class DiyPointCut { public void before() { System.out.println("------方法执行前------"); } public void after() { System.out.println("------方法执行后------"); } }
|
Spring配置文件aopContext2.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <?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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="org.example.learn.UserService"/>
<bean id="diy" class="org.example.learn.DiyPointCut" /> <aop:config> <aop:aspect ref="diy"> <aop:pointcut id="diyPointCut" expression="execution(* org.example.learn.UserService.*(..))"/> <aop:before method="before" pointcut-ref="diyPointCut" /> <aop:after method="after" pointcut-ref="diyPointCut" /> </aop:aspect> </aop:config> </beans>
|
测试
1 2 3 4 5 6
| @Test public void TestService2() { ClassPathXmlApplicationContext cx = new ClassPathXmlApplicationContext("aopContext2.xml"); service1 service = (service1) cx.getBean("userservice"); service.delete(); }
|
方法三
业务接口和实现类同上
编写注解实现的增强类AnnotationPointCut.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package org.example.learn;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;
@Aspect public class AnnotationPointCut {
@Before("execution(* org.example.learn.UserService.*(..))") public void before(){ System.out.println("方法执行前"); } @After("execution(* org.example.learn.UserService.*(..))") public void after(){ System.out.println("方法执行后"); }
@Around("execution(* org.example.learn.UserService.*(..))") public void around(ProceedingJoinPoint pj) throws Throwable{ System.out.println("环绕前"); System.out.println("签名:"+pj.getSignature()); Object proceed = pj.proceed(); System.out.println("环绕后"); System.out.println(proceed); } }
|
在spring配置文件aopContext3.xml中,注册bean,并增加支持注解的支持
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userservice" class="org.example.learn.UserService"/>
<bean id="annotationPointcut" class="org.example.learn.AnnotationPointCut"/> <aop:aspectj-autoproxy/> </beans>
|
通过aop命名空间的注解支持声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面
参考
Spring 中文文档 (springdoc.cn)
Java学习之Spring框架入门篇
Java学习之Spring框架基础篇
Bean的自动装配 - 天下御免 - 博客园 (cnblogs.com)
AOP及实现方式 - aishimin - 博客园 (cnblogs.com)
Spring AOP——Spring 中面向切面编程 - SharpCJ - 博客园 (cnblogs.com)