Spring5
1、Spring简介
1.1、Spring的概念
Spring是分层的JavaSE/EE应用Full-stack轻量级开源框架,以Ioc(Inverse Of Control:控制反转)和AOP(Aspect Oriented Programming:面向切面编程)为核心。
提供了展现层SpringMVC和持久层SpringJDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的JavaEE企业应用开源框架。
1.2、Spring发展历程
1997年,IBM提出了EJB的思想
1998年,SUN制定开发标准规范EJB1.0
1999年,EJB1.1发布
2001年,EJB2.0发布
2003年,EJB2.1发布
2006年,EJB3.0发布
Rod Johnson ( Spring之父)
Expert One-to-One J2EEDesign and Development(2002)
阐述了J2EE使用EB开发设计的优点及解决方案
Expert One-to-One J2EEDevelopment withoutEJB(2004)
阐述了J2EE开发不使用EB的解决方式(Spring雏形)
2017年9月份发布了Spring的最新版本Spring5.0通用版(GA)
1.3、Spring的优势
1)方便解耦
通过Spring提供的loC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度耦合。
用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2)AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现。
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
4)方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5)方便集成各种优秀框架
Spring对各种优秀框架(Struts、Hibernate、Hessian、Quart等)的支持。
6)降低JavaEE API的使用难度
Spring对JavaEEAPI(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低
- Java源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
1.4、Spring的体系结构
2、Spring快速入门
2.1、Spring程序开发步骤
-
Maven工程----->在pom.xml中导入Spring开发的基本包坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency>
-
编写Dao接口和实现类
UserDao
public interface UserDao { void save(); }
UserDaoImpl
public class UserDaoImpl implements UserDao { public void save() { System.out.println("Save running!"); } }
-
创建Spring核心配置文件
Maven工程----->在resources文件夹下创建Spring核心配置文件"beans.xml"
在beans.xml文件中导入spring-beans的约束
<?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"> </beans>
-
<?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="userDaoImpl" class="com.accp.dao.UserDaoImpl"></bean> </beans>
-
使用Spring的API获得Bean实例
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClasspathXmlApplicationContext("beans.xml"); //从context中获取bean对象 //方式一: UserDao daoImpl1 = context.getBean("userDaoImpl", UserDaoImpl.class); daoImpl1.save(); //方式二: UserDao daoImpl2 = (UserDaoImpl) context.getBean("userDaoImpl"); daoImpl2.save(); } }
3、Spring配置文件详解
3.1、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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.accp.dao.UserDaoImpl"></bean>
</beans>
用于配置对象交由Spring来创建。
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
基本属性:
3.2、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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.accp.dao.UserDaoImpl" scope="prototype"></bean>
</beans>
scope:指对象的作用范围
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中 |
session | WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中 |
global session | WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession相当 于session |
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean0方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被Java的垃圾回收器回收了
3.3、Bean声明周期配置
在Bean对象中编写两个任意名称的方法,一个在初始化对象时执行,一个在对象销毁时执行
public class UserDaoImpl implements UserDao {
public void init(){
System.out.println("对象初始化方法");
}
public void destory(){
System.out.println("对象销毁方法");
}
}
在beans.xml文件中进行配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.accp.dao.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destory"></bean>
</beans>
测试
@Test
public void test2(){
ApplicationContext context = new ClasspathXmlApplicationContext("beans.xml");
//需要手动关闭才能看到方法的销毁执行
((ClasspathXmlApplicationContext) context).close();
}
打印输出
3.4、Bean实例化三种方式
工厂静态方法实例化
编写静态工厂类及方法
public class StaticFactory {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.accp.factory.StaticFactory" factory-method="getUserDao"></bean>
</beans>
测试
无参构造方法实例化
编写一个类(UserDaoImpl.java)
public class UserDaoImpl {
public UserDaoImpl(){
System.out.println("UserDaoImpl被创建了");
}
}
配置beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDaoImpl" class="com.accp.dao.UserDaoImpl"></bean>
</beans>
测试
工厂实例方法实例化
编写工厂实例类
public class DynameicFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dynameicFactory" class="com.accp.factory.DynameicFactory"></bean>
<bean id="userDaoImpl" factory-bean="dynameicFactory" factory-method="getUserDao"></bean>
</beans>
测试
3.5、依赖注入(DI)
3.5.1、依赖注入概念
依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现。
IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务偿仍会调用持久层的方法。
在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。
那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
3.5.2、注入对象
将Dao层对象注入到Service层的两种方式
set注入
Service层
public class UserServiceImpl implements UserService {
//通过set方法注入
private UserDaoImpl userDao;
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
测试
还可以使用p命名空间注入
需要先引入p命名空间:xmlns:p="http://www.springframework.org/schema/p"
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="userDao" class="com.accp.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.accp.service.UserServiceImpl" p:userDao-ref="userDao"></bean>
</beans>
测试
通过构造方法注入
public class UserServiceImpl implements UserService {
//通过构造方法注入
private UserDaoImpl userDao;
public UserServiceImpl(UserDaoImpl userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
配置beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.accp.dao.UserDaoImpl"></bean>
<bean id="userService" class="com.accp.service.UserServiceImpl">
<constructor-arg ref="userDao"/>
</bean>
</beans>
测试
3.5.3、Bean的依赖注入数据类型
注入除了引用Bean注入还可以注入普通数据类型、引用数据类型、集合。
注入数据的三种数据类型
- 普通数据类型
- 引用数据类型
- 集合数据类型
普通数据类型注入
public class Person {
private int age;
private String name;
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
配置beans.xml注入属性值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.accp.pojo.Person">
<property name="name" value="孙奕秋"/>
<property name="age" value="22"/>
</bean>
</beans>
测试
List、Map、Properties注入
创建User.java
public class User {
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 "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建Person.java
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Person {
private List<String> stringList;
private Map<String, User> personMap;
private Properties properties;
public List<String> getStringList() {
return stringList;
}
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public Map<String, User> getPersonMap() {
return personMap;
}
public void setPersonMap(Map<String, User> personMap) {
this.personMap = personMap;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Person{" +
"stringList=" + stringList +
", \npersonMap=" + personMap +
", \nproperties=" + properties +
'}';
}
}
配置beans.xml进行值注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--创建两个User对象-->
<bean id="user1" class="com.accp.pojo.User">
<property name="name" value="克里斯"/>
<property name="age" value="30"/>
</bean>
<bean id="user2" class="com.accp.pojo.User">
<property name="name" value="曲忠辉"/>
<property name="age" value="50"/>
</bean>
<bean id="person" class="com.accp.pojo.Person">
<!--List集合注入-->
<property name="stringList">
<list>
<value>孙奕秋</value>
<value>吴恪之</value>
<value>林宇明</value>
</list>
</property>
<!--Map集合注入-->
<property name="personMap">
<map>
<entry key="演员1" value-ref="user1"></entry>
<entry key="演员2" value-ref="user2"></entry>
</map>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="driver">com.MysqL.jdbc.Driver</prop>
<prop key="url">jdbc:MysqL://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
测试
3.5.4、引入其他配置文件(分模块开发)
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
<?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">
<!--引入其他配置文件-->
<import resource="app.xml"/>
</beans>
引入properties类型的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入Propertis配置文件-->
<context:property-placeholder location="jdbc.properties"/>
</beans>
Spring的重点配置
<bean>标签
id属性:在容器中Bean实例的唯一标识,不允许重复
class属性:要实例化的Bean的全限定名
scope属性:Bean的作用范围,常用是singleton(默认)和prototype
<property>标签:属性注入
name属性:属性名称
value属性:注入的普通属性值
ref属性:注入的对象引用值
<list>标签
<map>标签
<properties>标签
<constructor-arg>有参构造函数标签
<import>标签:导入其他的spring的分文件
4、Spring相关API
4.1、ApplicationContext的集成体系
ApplicationContext:接口类型,代表应用上下文,可以通过其实例获得spring容器中的Bean对象
4.2、ApplicationContext的实现类
-
FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。 -
AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。
public static void main(String[] args) {
//加载xml配置文件 路径为项目下的相对路径 推荐使用
ApplicationContext context1 = new ClasspathXmlApplicationContext("beans.xml");
//加载注解配置类
ApplicationContext context2 = new AnnotationConfigApplicationContext("com.accp.service");
//加载xml配置文件 路径为系统的绝对路径 不推荐使用
ApplicationContext context3 = new FileSystemXmlApplicationContext("E:\\development\\IdeaProjects\\Spring_01\\src\\main\\resources\\beans.xml");
}
4.3、getBean()的使用
public static void main(String[] args) {
ApplicationContext context = new ClasspathXmlApplicationContext("beans.xml");
//指定id和类型,不需要进行类型转换
Person person1 = context.getBean("person", Person.class);
//指定id,需要进行类型转换
Person person2 = (Person) context.getBean("person");
//指定类型,不需要进行类型转换,前提是Beans容器中只有一个Person类型的bean
Person person3 = context.getBean(Person.class);
}
5、Spring配置数据源
5.1、数据源(连接池)的作用
常见的数据源(连接池):DBCP、C3PO、BoneCP、Druid等
5.2、手动配置数据源(C3P0)
导入坐标(jar包)
<dependency>
<groupId>MysqL</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.1</version>
</dependency>
编写Java代码
@Test //创建c3p0数据源
public void test1() throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.MysqL.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:MysqL://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("123456");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
System.out.println(connection);
}
打印输出
5.3、手动配置数据源(druid)
导入坐标(jar包)
<dependency>
<groupId>MysqL</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
编写Java代码
@Test //创建druid数据源
public void test2() throws Exception {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.MysqL.jdbc.Driver");
druidDataSource.setUrl("jdbc:MysqL://localhost:3306/test");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
DruidPooledConnection connection = druidDataSource.getConnection();
System.out.println(connection);
connection.close();
System.out.println(connection);
}
打印输出
5.4、使用配置文件配置数据源连接参数
Maven工程---->在resources文件夹下新建jdbc.properties文件,并补充以下代码
jdbc.driver=com.MysqL.jdbc.Driver
jdbc.url=jdbc:MysqL://localhost:3306
jdbc.username=root
jdbc.password=123456
编写Java代码
@Test //创建c3p0数据源 加载Properties配置文件
public void test3() throws Exception {
//读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
String driver = rb.getString("jdbc.driver");
String url = rb.getString("jdbc.url");
String username = rb.getString("jdbc.username");
String password = rb.getString("jdbc.password");
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
System.out.println(connection);
}
打印输出
5.5、使用Spring配置数据源
导入坐标(jar包)----使用C3P0数据源为例,druid也一样
<dependency>
<groupId>MysqL</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
配置beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.MysqL.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:MysqL://localhost:3306/test"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
</beans>
测试
4
为了更加解耦,可以通过beans.xml文件引用jdbc.properties来配置数据源
编写jdbc.properties
jdbc.driver=com.MysqL.jdbc.Driver
jdbc.url=jdbc:MysqL://localhost:3306
jdbc.username=root
jdbc.password=123456
配置beans.xml,引入jdbc.properties
beans.xml引入properties需要引入约束
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入jdbc.properties-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
测试
6、Spring注解开发
6.1、注解的优势
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
6.2、原始注解
Spring原始注解主要是替代
注解 | 说明 |
---|---|
@Component | 使用类上用于实例化Bean |
@Controller | 使用在web层用于实例化Bean |
@Service | 使用在Service上用于实例化Bean |
@Repository | 使用在Dao层上用于实例化Bean |
@Autowired | 使用在字段上用于根据类型依赖注入 |
@Qualifier | 结合@Autowried一起使用用于根据名称进行依赖注入 |
@Resource | 相当于@Autowired+@Qualifier,按照名称进行注入 |
@Value | 注入普通属性 |
@Scope | 标注的Bean的作用范围 |
@postconstruct | 使用在方法上标注该方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注该方法时Bean的销毁方法 |
注解的使用
dao层
//@Component("userDao")
@Repository("userDao") //实例化UserDaoImpl并赋予bean的id为userDao
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save running!");
}
}
service层
//@Component("userService")
@Service("userService") //实例化UserServiceImpl并赋予bean的id为userService
@Scope("singleton") //设置该bean的作用域为单例模式
public class UserServiceImpl implements UserService {
//@Autowired
//@Qualifier("userDao")
@Resource(name="userDao") //@Autowired + @Qualifier("userDao") = @Resource(name="userDao")
private UserDao userDao;
@Value("${jdbc.url}") //为driver赋值为配置文件的jdbc.url值,前提是在beans.xml中引入这个配置文件
private String driver;
public void save() {
System.out.println(driver);
userDao.save();
}
@postconstruct //相当于init-method 在对象初始化时执行的方法
public void init(){
System.out.println("UserServiceImpl创建初始化方法");
}
@PreDestroy //相当于destory-method 在对象销毁时执行的方法
public void destory(){
System.out.println("UserServiceImpl销毁方法");
}
}
beans.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--配置组件扫描:扫描com.accp包下的所有bean-->
<context:component-scan base-package="com.accp"/>
</beans>
测试
注解注意事项:
- 使用注解为属性赋值时可以不用封装属性的setter方法
- 使用注解完成后还应该在beans.xml中配置组件扫描,这样注解才能使用生效
- 如果想看到销毁方法执行,那么需要手动关闭ClasspathXmlApplicationContext对象
6.3、新注解
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时要扫描的包。 作用和在Spring的xml配置文件中的 <context:component-scan base-package="com.itheima"/>—样 |
@Bean | 使用在方法上,标注将该方法的放回值存储到spring容器中 |
@PropertySource | 用于加载.properties文件中的配置 |
@Import | 用于导入其他配置类 |
新注解的使用
@Import({DataSourceConfiguration.class}) //导入DataSourceConfiguration配置类
@Configuration //该标志是Spring的核心配置类
@ComponentScan("com.accp") //== <context:component-scan base-package="com.accp"/>
@PropertySource("classpath:jdbc.properties") //加载配置文件
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource") //将该方法的放回值存储到spring容器中
public DataSource getDataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
测试
@Test
public void test1() throws sqlException {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
DataSource dataSource = context.getBean("dataSource", DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
//手动关闭容器---这样才能看到bean的销毁方法执行
((AnnotationConfigApplicationContext) context).close();
}
注意:加载Spring注解配置类用AnnotationConfigApplicationContext实现类
7、Spring整合Junit
原始Junit测试Spring的问题
在测试类中,每个测试方法都有以下两行代码:
Applicationcontext ac = new ClasspathXmlApplicationcontext("bean.xml");
Accountservice as = ac.getBean("accountservice",Accountservice.class);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
上述问题解决思路:Spring集成Junit
7.1、Spring集成Junit
让SpringJunit负责创建spring容器,但是需要将配置文件的名称告诉它将需要进行测试Bean直接在测试类中进行注入
7.1.1、 Spring集成Junit步骤
-
导入spring集成Junit的坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
-
使用@Runwith注解替换原来的运行期
-
使用@Autowired注入需要测试的对象
-
创建测试方法进行测试
@RunWith(SpringJUnit4ClassRunner.class) //@ContextConfiguration("classpath:beans.xml") @ContextConfiguration(classes = SpringConfigruation.class) public class MyTest { @Autowired private UserService userService; @Test public void test(){ userService.save(); } }
使用说明:
- @ContextConfiguration("classpath:beans.xml") 是加载xml配置文件
- @ContextConfiguration(classes = SpringConfigruation.class)是加载注解实现类
- @Autowired在需要测试的bean对象上添加
8、AOP
8.1、AOP简介
AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
8.2、AOP的作用及其优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
8.3、AOP的底层实现
实际上,AOP的底层是通过Spring提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。
8.4、AOP的动态代理技术
常用的动态代理技术
基于JDK的动态代理技术
创建目标对象接口(TargetInstance)
public interface TargetInstance {
void save();
}
创建TargetInstance接口实现类
public class Target implements TargetInstance{
public void save() {
System.out.println("save running !");
}
}
创建增强对象(Advice)
public class Advice {
public void before() {
System.out.println("增强前");
}
public void after() {
System.out.println("增强后");
}
}
实现动态代理并测试
public class Test {
public static void main(String[] args) {
final Target target = new Target();
final Advice advice = new Advice();
TargetInstance proxy = (TargetInstance) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象类加载器
target.getClass().getInterfaces(), //目标对象接口类加载器
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();
Object invoke = method.invoke(target, args);
advice.after();
return invoke;
}
}
);
proxy.save();
}
}
基于cjlib的动态代理技术
先导入cjlib坐标---Spring-context集成cjlib
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
创建目标对象(Target)
public class Target {
public void save() {
System.out.println("save running !");
}
}
创建增强对象(Advice)
public class Advice {
public void before(){
System.out.println("增强前");
}
public void after(){
System.out.println("增强后");
}
}
实现动态代理并测试
public class Test {
public static void main(final String[] args) {
final Target target = new Target();
final Advice advice = new Advice();
//1.创建增强器
Enhancer enhancer = new Enhancer();
//2.设置父类(目标)
enhancer.setSuperclass(Target.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行前置
advice.before();
//执行目标
Object invoke = method.invoke(target, args);
//执行后置
advice.after();
return invoke;
}
});
//4.创建代理对象
Target o = (Target) enhancer.create();
o.save();
}
}
两种动态代理的特点:
- JDK的动态代理实现依赖于接口
- cjlib的动态代理实现依赖于父类
8.5、AOP相关概念
Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。
在正式讲解AOP的操作之前,我们必须理解AOP的相关术语,常用的术语如下:
- Target(目标对象)︰代理的目标对象
- Proxy(代理)∶一个类被AOP织入增强后,就产生一个结果代理类
- pointcut(切入点)∶所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
- Joinpoint(连接点)︰所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
- Advice(通知/增强)∶所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
- Aspect(切面)︰是切入点和通知(引介)的结合
- Weaving(织入)︰是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
8.6、AOP开发明确事项
需要编写的内容
AOP技术的实现内容
Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
AOP底层使用哪种代理技术
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式
8.7、基于XML的AOP开发
快速入门
-
导入AOP相关坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.4</version> <scope>runtime</scope> </dependency>
-
创建目标接口和目标类(内部有切点)
目标接口:TargetInstance
public interface TargetInstance { void save(); }
目标类:Target
public class Target implements TargetInstance{ public void save() { System.out.println("save running !"); } }
-
创建切面类(内部有增强方法):MyAspect
public class MyAspect { public void before(){ System.out.println("前置增强"); } public void after(){ System.out.println("后置增强"); } }
-
将目标类和切面类的对象创建权交给spring
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标对象 --> <bean id="target" class="com.accp.aop.Target" init-method="init" destroy-method="destory"></bean> <!-- 切面对象 --> <bean id="myAspect" class="com.accp.aop.MyAspect"></bean> </beans>
-
在applicationContext.xml中配置织入关系
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 目标对象 --> <bean id="target" class="com.accp.aop.Target" init-method="init" destroy-method="destory"></bean> <!-- 切面对象 --> <bean id="myAspect" class="com.accp.aop.MyAspect"></bean> <!-- 配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置,后置) --> <aop:config> <!-- 声明切面 --> <aop:aspect ref="myAspect"> <!-- 切面:切点+通知 --> <aop:pointcut id="myref" expression="execution(public void com.accp.aop.Target.save())"/> <!-- 前置增强--> <aop:before method="before" pointcut-ref="myref" /> <!-- 后置增强--> <aop:after method="after" pointcut-ref="myref" /> </aop:aspect> </aop:config> </beans>
-
测试代码
public static void main(String[] args) { ApplicationContext context = new ClasspathXmlApplicationContext("applicationContext.xml"); TargetInstance target = context.getBean("target", TargetInstance.class); target.save(); }
切点表达式的写法
表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号*代表任意
- 包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
- 参数列表可以使用两个点..表示任意个数,任意类型的参数列表
例如
execution(public void com.accp.aop.Target.method())
execution (void com.accp.aop.Target.*(..))
execution (* com.accp.aop.*.*( ..))
execution(* com.accp. aop..*.*(..))
execution(* *..*.*(..))
通知的类型
通知的配置语法:
<aop:通知类型method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 | aop:before | 用于配置前置通知。指定增强的方法在切入点方法之前执行 |
后置通知 | aop:after-returning | 用于配置后置通知。指定增强的方法在切入点方法之后执行 |
环绕通知 | aop:around | 用于配置环绕通知。指定增强的方法在切入点方法之前和之后都 执行 |
异常抛出通知 | aop:throwing | 用于配置异常抛出通知。指定增强的方法在出现异常时执行 |
最终通知 | aop:after | 用于配置最终通知。无论增强方式执行是否有异常都会执行 |
8.8、基于注解的AOP开发
快速入门
基于注解的aop开发步骤:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。