一、Spring框架的介绍
Spring是一个分层的JavaSE/JavaEE应用full-stack轻量级开源框架
- 分层
Spring提供了很多的技术,我们可以单独的使用某一个技术,也可以一起用
- javaSE/javaEE
Spring在技术的覆盖度很广,在很多地方都有Spring技术的体现
- full-stack一站式
这样Spring提供一站式的解决方案,如果全部用Spring的解决方案那么效率会很高
- 轻量级
-
二、Spring的体系结构
底层是核心容器(这个是最重要的是Spring的基础)包含一下几个技术
- Beans
- core
- Context
- SpringEl表达式
- 中间层技术
- AOP
- Aspects
- Instrumentation
- Messaging
- 应用层技术
- Data Access/Integration(集成)(数据层解决方案:数据访问与数据集成)。集成Mybatis等
- Web集成(集成Structs,集成SpringMVC等)
- Test:Spring提供测试

总结:Spring底层是一个Spring的核心容器,往上就是Spring提供一些中间层技术例如AOP,Aspects等,在往上是就是提供一些集成第三方的技术(集成持久层技术,例如Hibernate,Mybatis,集成Web层技术例如SpringMVC,Struts2等),即它的核心就是在做集成,它的作用是整合其他技术
三、spring体系结构技术
3.1、IOC容器
背景:我们设计程序会有多个模块,不同模块具有不同的功能,处理不同的问题,而我们在设计这些模块的时候尽量让这些模块分离开来,不要相互依赖,而对于模块内部彼此之间相互结合,即高内聚低耦合。这样设计的程序更容易维护
传统的功能调用,我们是使用new的方式创建对象,然后通过对象调用其方法,这样写如果我们的类换了,那么原来new的地方都要更改,要改变源代码,源代码需要重写编译然后发布,这样类之间的调用属性紧耦合,我们可以通过工厂模式,通过工厂来创建对象这样如果我们修改类的话,就不需要修改调用方的源码,只需要修改工厂中的源码这样修改的地方是少了但是依然要修改源码,进行重新编译发布,在此基础上我们通过工厂模式利用反射加配置文件的方式,我们将需要调用的类写到配置文件中,通过读取配置文件来创建对象,这样每次修改类我们就直接修改配置文件即可,因为配置文件不需要编译,打包等操作,我们可以直接发布到服务器读取即可,这样就降低了程序之间的耦合性——--这用思想就是Spring的雏形,Spring中最核心的思想IOC
IOC(控制反转):对于传统模式我们是通过主动new对象的方式创建资源,这个时候资源的创建是类把控的,而对于SpringIOC,我们将资源的创建交给IOC容器,当我们需要调用的时候,不需要我们创建,直接去IOC容器中去取即可,这个使用资源的对象是由Spring把控的,把对象的创建交给Spring,我们只需要处理对象调用的逻辑即可,Spring帮我们做对象的创建和销毁
IOC总结:Spring反向控制外部资源
**
四、Spring使用案例
4.1、SpringIOC容器使用基本使用
导入坐标:这个是SpringIOC容器的坐标,也是Spring的核心坐标
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency>
创建需要被Spring管理的类
编写Spring的配置文件(在resources目录下,注意Spring的和兴配置文件名叫applicationContext.xml)
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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 配置Bean对象:将对象的创建交给Spring管理Spring管理的对象是以Bean的形式配置的,也就是说Spring管理的是一个个的Bean对象,我们通过配置Bean对象来确定那些类需要交给Spring管理;id表示Bean对象的唯一标识,一个配置文件中的Bean的id不能重复,我们可以通过这个Id来获取Bean对象,一般用接口命名class:表示我们要将那个类交给Spring管理,这里要写全类名--><bean id="userService" class="com.study.service.impl.UserServiceImpl"/></beans>
在配置文件中声明Bean对象(即将对象创建交给Spring管理)
- 获取Bean对象,调用方法 ```java package ioc;
import com.study.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //这个测试是Spring提供单元测试,可以测试Spring模块 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(“classpath:applicationContext.xml”) public class SpringIocTest { @Test public void testGetBean(){ //加载配置文件 ApplicationContext ctx= new ClassPathXmlApplicationContext(“applicationContext.xml”); //获取Bean对象,通过Id获取 UserService userService = (UserService) ctx.getBean(“userService”); userService.save(); } }
<a name="TbmcV"></a>
### 五、Spring各种的配置详解
<a name="85d55405"></a>
#### 5.1、Spring中Bean的配置
1.bean标签它是用于定义Spring中的资源,表示该资源被Spring控制管理,我们不仅可以通过Bean的id获取Bean也可以通过name获取Bean,name的值可以是多个,之间用逗号隔开相当于起别名<br /><br />UserService userService = (UserService) ctx.getBean(**"userService1"**);//可以根据name获取Bean对象<br />**2.Bean标签的属性值:scope,它是用来定义Bean对象的创建是不是单例的,域对象的范围(默认创建就是单例)**<br />**注意单例的和非单例的它们创建的时机也不同:单例对象是在创建Spring容器的时候创建Bean对象,如果是非单例的只有在我们获取对象的时候才会创建,获取一次创建一次**<br />******
```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">
<!-- 默认scope的值是singleton,即单例创建bean对象
-->
<bean id="userService" name="userService1,userService2" scope="prototype" class="com.study.service.impl.UserServiceImpl"/>
</beans>
3.Bean对象创建的声明周期(即通过Bean对象的声明周期,我们可以实现在创建Bean之前实现一些功能,和Bean要销毁之前实现一些功能)
由于是我们Bean对象的声明周期,所以我们可以在要创建Bean对象的类中定义相关声明周期方法,在创建Bean的时候来执行这些Bean对象中的方法实现Bean对象的初始化和销毁操作
通过init-method可以实现给static属性进行赋值,即声明init-method指定的方法为静态方法,然后在静态方法中进行初始化static变量
<bean id="userService" destroy-method="destroy" init-method="init" class="com.study.service.impl.UserServiceImpl"/>
<!-- init-method这个属性是创建对象时执行,即先创建对象,然后执行init方法,对象创建几次init-method执行几次,单例执行一次
当spring容器关闭的时候,会执行destroy-method方法,注意:如果我们的Bean对象声明为非单例的,那么这个对象将不归Spring容器管理,spring也不会主动销毁创建的Bean对象
-->
4.工厂方式创建Bean对象(使用于以前没有使用Spring,使用的是工厂模式创建对象)
如果原来的代码没有使用Spring,而是使用的工厂模式创建对象,我们可以在不改变原有代码的情况下,将工厂交给Spring管理,由Spring创建对象
静态工厂交给Spring
package com.study.fatory;
import com.study.service.UserService;
import com.study.service.impl.UserServiceImpl;
//用来创建UserService对象的工厂
public class UserFactory {
public static UserService getUserService(){
return new UserServiceImpl();
}
}
将工厂交给SPring管理(注意Spring是以Bean的方式管理对象,所以我们要想交给Spring管理就是配置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">
<!-- 将工厂交给Spring管理,即配置Bean
这里一定要指明创建对象的是工厂的那个方法,因为工厂可能有很多个方法
-->
<bean id="userFactory" class="com.study.fatory.UserFactory" factory-method="getUserService"/>
</beans>
实例工厂交给Spring(由于实例工厂,创建对象的方法是非静态的,所以我们需要使用对象进行调用,所以我们需要先创建工厂的Bean对象,即将工厂交给SPring管理,然后将工厂创建的对象给我们要创建的Bean对象,进行引用,通过工厂对象来创建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">
<!-- 先创建工厂类的对象-->
<bean id="beanFactory" class="com.study.fatory.UserFactory2"/>
<!-- 将工厂对象注入我们要创建的Bean对象,注意Spring中Bean的Id就是Bean对象的唯一标识,所以我们使用id 注入
factory-bean就是填入我们实例工厂对象
-->
<bean id="userService" factory-bean="beanFactory" factory-method="getUserService"/>
</beans>
5.2DI(dependency injection)
注意:SPring的 DI注入可以直接给集合注入资源,如果是List集合,直接注入集合泛型的bean ,如果是Map集合,那么Map集合的key是bean的ID,Value是Bean对象
概念理解:IOC(即将Spring的管理的资源作为参数进行传参)
IOC(inversion of control):表示控制反转,我们站在Spring的角度,我们将需要用的资源交给Spring,Spring管理我们需要的资源这叫IOC,将Spring管理的资源注入到我们的应用程序我们称为依赖注入,即我们可以使用Spring管理资源给我们应用程序资源赋值(即给对象的属性赋值)
1.set注入(我们可以将SPring管理的资源注入到Bean对象):通过property属性
要求:即我们要给对象属性赋Spring管理的资源,那么该对象需要被Spring管理,且提供对应的set方法
public class UserServiceImpl {
//这个属性我们可以使用DI注入,在创建Bean对象的时候给其赋值
private UserDao userDao;
//这个属性我们可以使用DI注入,在创建Bean对象的时候给其赋值
private String version;
public void setVersion(String version) {
this.version = version;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
<?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">
<!-- 创建我们需要注入的资源该资源如果是引用数据类型(String除外),需要交给Spring管理-->
<bean id="userDao" class="com.study.dao.impl.UserDaoImpl"/>
<!-- 给Bean对象注入资源-->
<bean id="userService" class="com.study.service.impl.UserServiceImpl">
<!--通过property属性来给Bean对象属性赋值
name:需要填属性名,表示给那个属性赋值,ref、value表示赋的具体值,如果值是基本数据类型(包括String),使用value,如果是引用数据类型,需要使用ref,这个引用数据类型,必须是Bean对象,ref的值需要填Bean对象的Id
-->
<property name="userDao" ref="userDao"/>
<property name="version" value="test"/>
</bean>
</beans>
2.构造方法注入:(我们初始化对象给对赋值也有两种方式,一种是先创建对象在调用set()方法,另一种就是创建对象时使用构造器赋值)
使用构造器给Bean对象参数赋值,name是指给那个参数赋值,ref/或者value和上面set方法一样。注意构造器赋值,一定要提供对应的构造器,constructor-arg 还有一个index参数用来指定给构造器赋值的参数顺序,index从开始,一般使用name不用index,还有一个参数是Type指赋值的参数类型
<bean id="userService1" class="com.study.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
</bean>
3.不同类型的注入
不同的数据类型,我们注入方式也不同,基本数据类型我们就用value,引用数据类型我们使用ref,但是引用数据类型对于集合注入方式也不同
3.1.list集合注入,我们还是通过property这个属性里继续写,注意,array注入方式可以和list一样,所以我们可以用同一种
3.2properties类型
3.3、set集合注入
3.4map集合注入
如果集合的泛型是引用数据类型,我们依然使用ref标签,只不过赋值使用的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">
<!-- 创建我们需要注入的资源该资源如果是引用数据类型(String除外),需要交给Spring管理-->
<bean id="userDao" class="com.study.dao.impl.UserDaoImpl"/>
<!-- 给Bean对象注入资源-->
<bean id="userService" class="com.study.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="version" value="test"/>
</bean>
<!-- 声明一个Bean然后注入各种类型-->
<bean id="bookDao" class="com.study.dao.impl.BookDaoImpl">
<!-- 给这个对象的属性进行注值:注意注值如果我们使用的是set方法的方式,那么需要使用property-->
<!-- List注入-->
<property name="list">
<list>
<!-- 这里注入基本数据类型使用value,引用数据类型使用ref,也可以注入bean-->
<value>aa</value>
<value>bb</value>
<ref bean="userDao"/>
<bean class="com.study.service.impl.UserServiceImpl"/>
</list>
</property>
<!-- 注意Properties类型只有String类型-->
<property name="properties">
<props>
<prop key="test1">aa</prop>
<prop key="test2">bb</prop>
</props>
</property>
<!-- array的注入方式和list的方式相同-->
<property name="array">
<list>
<value>aa</value>
</list>
</property>
<property name="set">
<set>
<value>aa</value>
<value>BBB</value>
</set>
</property>
<property name="map">
<map>
<entry key="test" value="aa"/>
<entry key="test1" value="bb"/>
</map>
</property>
</bean>
</beans>
5.3P命名空间(用来简化配置)
1.引用头文件(来支持命名空间)
2.P命名空间它是用来简化我们的properties属性的,我们可以不用写properties的属性,例如key和value,通过P:加属性值的方式来进行简化(如果是引用数据类型,需要写成p:属性值-ref)
5.4SpringEL,我们属性的注入可以写成EL表达式的形式,前提需要引入EL
5.5、读取外部配置文件(重要)
我们在配置数据库连接等,需要写properties文件,我们可以通过Spring来读取Properties文件
<!-- 读取外部配置文件:
spring的头文件都是xmlns:...,如果我们要引入p命名空间就是xmlns:p,这次我们引入的是context所以需要xmlns:context
同时头文件的支持都是读取的/schema这个目录下,我们引入context,所以就需要schema/context
context它是一个标签,所以我们还需要加入配置才能引入,这个配置和引入Beans的配置一样
第一步引入头文件支持xmlns:context="http://www.springframework.org/schema/context"
第二步引入外部配置文件:这里的classpath表示类路径,*表示匹配所以-->
<context:property-placeholder location="classpath*:*.properties"/>
<!-- 第三步:我们将配置文件引入后就可以读取配置文件了,这里使用set方法注入-->
<bean id="propertiesService" class="com.study.service.impl.PropertiesServiceImpl">
<!-- 通过${key}来去读配置文件中key对应的value-->
<property name="username1" value="${user}"/>
<property name="password1" value="${pwd}"/>
</bean>
5.6、配置文件的导入(import标签)
我们在进行团队开发时,不同的人可能使用的不同的配置文件不同,这样团队开发上传的时候可能导致配置文件冲突,为了解决这一问题,我们可以写一个主配置文件,通过主配置文件来引入子配置文件,这样不同的人需要不同的配置文件我们在开发的引入子配置文件即可,这样就可以根据我们的需要来加载不同的配置文件
<!-- 引入子配置文件-->
<import resource="testContext.xml"/>
5.7、Spring核心对象的层次结构

Spring以前使用的是BeanFactory来获取Bean对象(工厂模式),后来通过ApplicationContext来获取Bean对象,并添加了一些新的功能,例如消息服务等,他俩的区别是BeanFactory采用的是延迟加载的形式获取bean,我们在getBean的时候才会获取对象,而ApplicationContext采用的是立即加载的形式,我们在创建IOC容器的时候创建bean 对象,
ClassPathXmlApplicationContext这个对象是用来加载核心配置文件的,与它功能相同的还有FileSystemXmlApplicationContext这个类,它和上面那个类功能完全相同也是new出来的,但是这个里是用来加载系统盘里的配置文件,例如C盘下的
5.7、配置第三方资源(即将第三方资源交给Sring管理)
这个和配置自己的Bean一样,也是使用Bean标签,然后使用set或者构造器注入属性值,常用的是给配置数据源
六、Spring整合mybatis(不加事务)
第一步导入坐标 ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">4.0.0 com.study spring-mybaits 1.0-SNAPSHOT <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency><dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.6</version> </dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.15.RELEASE</version> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies>
- 编写核心核心配置文件
```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
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 id="userService" class="com.study.service.impl.UserServiceImpl"/>-->
<!-- 第一步:导入外部资源文件-->
<context:property-placeholder location="jdbc.properties"/>
<!-- 第二步:配置数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${user.driverClass}"/>
<property name="url" value="${user.url}"/>
<property name="username" value="${user.username}"/>
<property name="password" value="${user.password}"/>
</bean>
<!--
第三步:配置SqlSessionFactoryBean对象交给Spring管理,注入数据源同时配置别名等
这个对象是用来获取SQLSession对象
这个类来之mybatis整合Spring的包
-->
<bean id="sessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.study.domain"/>
</bean>
<!-- 配置映射,这个配置用来扫描代理接口,从而生成代理对象注入Bean,这个类来之mybatis整合Spring的包-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.study.dao"/>
</bean>
<!--配置ServiceBean,注入Mybatis代理对象,进行测试-->
<bean id="userService" class="com.study.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
第一步:开启注解支持(即开启包扫描,扫描Spring的注解),在核心配置文件中加入包扫描,注意该注解是扫描包,和这个包对应的子包,只扫描Java文件,和Spring所能识别的注解,扫描范围不宜过大,影响性能,它是使用递归的方式进行扫描
<context:component-scan base-package="com.study"/>
第二步:使用注解配置Bean
@Component@Controller:加在controller层@Service:加在service层@Repository:加在dao层
这四个注解本质没有区别都是将下面的类交给Spring管理,只是出于规范我们对应加注解,注解的value值就是Bean的id,如果不加那么这个id就默认是类名或接口名首字母小写
@Scope:这注解也是加在类上的,它对应的是Bean的scope属性,用来声明这个Bean是否为单例的,或者声明在web容器的位置,他的value值用来表示属性,默认是singleton
@Postconstruct这个注解是放在,类中方法上,它代替的就是Bean的init-method属性,用来指定初始化方法
@PreDestroy这个注解也是加在
package com.study.service;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
@Scope("prototype")
public class UserServiceImpl {
public void save(){
System.out.println("save is running....");
}
@PostConstruct
public void init(){
System.out.println("init....");
}
@PreDestroy
public void destroy(){
System.out.println("destroy....");
}
}
7.2使用注解配置第三方的Bean

对于加在第三方的Bean,我们需要自己提供对应获取地方对象的方法,然后在这个方法上加@Bean(value=”beanName”)注解,这样就可以将这个方法创建的Bean对象放入IOC容器中,注意光加这个Bean注解是不可以,Spring识别不了,包含这个注解的类也必须被Spring管理
package com.study.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@Component
public class DataSourceConfig {
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://182.92.71.236:3306/study");
dataSource.setUsername("root");
dataSource.setPassword("zhubowenmysql");
return dataSource;
}
}
7.3spring注入的注解
基本数据类型:注值使用@Value(“要注入的值”),这个注解可以加在属性上,也可以加在set方法上,用来给基本数据类型赋值,就相当于Bean标签的Propery属性,也可以通过${key}来读取外部资源文件,使用这个@Value注解和Property属性的区别就是@Value注解注值可以不需要set方法
引用数据类型注值:
@Autowired这个注解直接加到引用数据类型的属性,直接可以给引用数据类型赋值,不需要set方法
我们不需要指定注入的名字,它会根据要注入数据类型进行匹配注入,如果类型相同有多个Bean,那么它会根据属性名进行匹配,如果属性名和Bean的Id相同就注入,注意@Autowired不能自己主动指定名称,可以使用@Qualifier(“beanId”)来指定注入的Bean,如果我们配置Bean的时候不指定名称,那么这个Bean的就是类名首字母小写,注意这里是类的名字首字母小写,
@Primary表示优先加载,当使用@Autowired自动装配的时候,它会更具属性的类型先装配,而我们在创建引用类型的变量的时候都是使用接口进行声明,这个他就会更具接口的类型进行装配,将该该接口的实现了进行注入,如果接口只有一个实现那么它会直接注入,如果接口有多个实现类它会更具实现类的BeanId和要注入的属性变量名进行匹配相同就注入,这个使用也可以自己主动指定BeanId,如果自己指定的BeanId和要注入的属性名不一样,且有多个Bean的时候,这个时候可以使用@Qualifier(“BeanId”)注解来手动指定要注入的Bean,如果没有手动指定Bean,它会根据类型匹配,配到多个时,可以指定Bean加载优先级,这样就会加载优先级高的bean进行注入,注意优先级低的,也会加载可以获取到
7.4、Spring加载配置文件注解

package com.study.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
//加载resource目录下的配置文件
@PropertySource("classpath:jdbc.properties")
这里不支持通配符的格式,如果有多个,这里可以传数组
//@PropertySource({"classpath:jdbc.properties","classpath:jdbc1.properties"})
public class PropertiesService {
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
public void save(){
System.out.println(username+":"+password);
}
}
7.5、Spring核心配置文件替换成注解

我们在类上加上@Configuraion注解那么这个类就可以当成核心配置文件使用,通过@ComponentScan来配置包扫描,来扫描Spring注解所在的包这样就可以实现纯注解开发
7.6、导入数据源到配置类
通过@Import(配置数据源的类.class),这样我们原来配置数据源的类的就可以不用加@Component注解了
如果需要导入多个,那么这里填的就是数组
7.7、Bean的依赖加载
Bean的加载是有顺序的,我们可以控制谁先加载,谁后加载
@DepencesOn表示依赖那个Bean,这个注解表示依赖某个Bean,如果加载到类上表示这个类的Bean都依赖@DepencesOn指定的Bean,只有当依赖的Bean先加载,然后这些Bean才会加载,即A类上添加@DepencesOn(B),那么B就会先加载,然后A才会加载
这个注解可以加载类上也可以加载方法上
如果加在方法上,表示DepencesOn指定的Bean优于该方法上的@Bean的这个Bean的加载循序
如果加载类上,那么表示DepencesOn指定的Bean优于这个类中所有@bean的这些Bean的加载顺序
如果这个类加了@component注解,表示@DepencesOn优于@Component这个注解声明的Bean的加载
7.8、注解配置配置类的加载顺序
配置类指替换核心配置文件的类称为配置类
@Order(int number):这个Number值越小越先加载
ClassPathXmlApplicationContext ctx=new AnnotationConfigApplicationContext(SpringCofig.class,SpringConfig1.class);
//这里填的是一个可变形参,用来加载多个配置类
7.9SpringBean的延迟加载
八、Spring注解整合第三方
8.1、Spring整合Mybatis
第一步:使用@Configuration来替代核心配置文件
package com.study.config;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
@Configuration
@ComponentScan("com.study")
//在这里加载配置文件,其他配置类也能读到
@PropertySource("classpath:jdbc.properties")
@Import({JDBCConfig.class,MybatisConfig.class})
public class SpringConfig {
}
第二步配置数据源
package com.study.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class JDBCConfig {
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.driver}")
private String driver;
@Bean
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
return dataSource;
}
}
第三步:配置Mybatis的核心配置类
package com.study.config;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class MybatisConfig {
//配置别名
@Bean
public SqlSessionFactoryBean getSqlSessionFactory(@Autowired DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.study.domain");
return sqlSessionFactoryBean;
}
//配置代理接口
@Bean
public MapperScannerConfigurer getMapper(){
MapperScannerConfigurer mapper = new MapperScannerConfigurer();
mapper.setBasePackage("com.study.dao");
return mapper;
}
}
第四步:编写测试类进行测试
8.2:、Spring整合Juint
package com.study;
import com.study.config.SpringConfig;
import com.study.dao.UserDao;
import com.study.domain.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
//配置Spring测试类的类加载器
@RunWith(SpringJUnit4ClassRunner.class)
//指定使用SPring的那个核心配置类,或者核心配置方法
@ContextConfiguration(classes = SpringConfig.class)
public class SpringTest {
@Autowired
private UserDao userDao;
//标准的测试应该使用断言的方式
@Test
public void test(){
User user = userDao.findById(1);
//使用断言方式进行测试,参数一是期望值,参数二是实际值,
Assert.assertEquals("朱博文",user.getUsername());
}
@Test
public void test1(){
List<User> list = userDao.findAll();
Assert.assertEquals("9",list.size());
}
}
九、ComponentScan排除Bean的加载
我在测试自己的功能时,别人的功能可能会影响自己,这时候可以排除其他Bean,
@ComponentScan(
value = “com.study”,
excludeFilters =@ComponentScan.Filter(
type=FilterType.CUSTOM,
classes = MyFilter.class )
)
这里可以选着我们自定义的过滤器
package com.study.filter;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
//自定义Bean过滤器
public class MyFilter implements TypeFilter {
//这里返回false就表示都不过滤,所有的Bean都加载,如果返回true,就表示都过滤,所有的Bean都不加载
//MetadataReader metadataReader表示元数据,我们可以通过元数据来加载Bean的数据,然后
//根据这些数据来判断哪些Bean需要过滤
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
if(className.equals("com.study.impl.UserServiceImpl")){
return true;
}
return false;
}
}
十、自定义导入器
我们配置Bean要么使用XML配置,要么使用注解,如果很大量的Bean需要导入,这个时候一个一个的配置很麻烦,这个时候可以自定义导入器来实现
package com.study.selector;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MySelector implements ImportSelector {
//这里返回一个字符串数组,就是我们要导入的类的全路径类名
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.study.dao.UserDao"};
}
}
我们自定义的导入器要想生效必须在配置类中导入即:@Import(MySelector.class)
十一、自定义注册器
这个自定义注册器,就相当于@ComponnectScan这个注解的作用
十二、Bean的初始化解析

对于Bean的初始化我们可以使用Init-method方法,但是如果每一个bean都需要进行初始化方法,那么我们可以实现BeanPostProcessor这个接口来实现,不仅如此,Spring和提供了其他Bean或者Bean工厂初始化时需要执行操作的接口,注意:我们配置这个写都是属于配置类,到需要在Spring核心配置类中通过@Import进行引入才能使用
Bean创建之前,和创建之后执行的操作
最后一个InitializingBean这个接口是我们定义Bean的类自己实现的接口,这里的方法是需要强制实现的可以定义Bean创建之前
十三、简化Bean的创建
如果我们创建的Bean内部很繁琐,可以提前定义一个类来实现FactoryBean来简化Bean的创建
例如我们使用的Mybatis中SessionFactoryBean他就是为了创建SqlSession对象的
十四、AOP
14.1、AOP介绍
aop(aspect oriented Programing)即面向切面编程,是一种思想,属于软工范畴,用来知道开发如何组织程序结构,AOP思想是AOP联盟提出来的,Spring只是根据这个思想做了一个实现。
AOP的作用:可以提取出功能模块中的共性功能,实现插拔式组件体系结构
14.2、AOP中的概念
1.连接点:所有类中的方法称为连接点
2.切入点:需要提取功能的方法,也就是需要使用AOP进行增强功能的方法我们称为切入点
3.通知:我们提取的共性功能叫通知
4.切面:即我们提取共性功能的位置
5.目标对象:我们进行AOP切过后的对象称为目标对象
6.织入:目标对象少了共性功能无法工作,需要把共性的功能在放回去,这个过程叫织入
7.代理:织入过后产生的是一个代理对象,通过这个代理对象来执行相应的功能
8.引入:在这个代理类中,我们可以新加一些新的属性和方法,我们称为引入
14.3、AOP的入门案例(XML格式)
注意:这是SPringAOP,所以要实现AOP的功能,这些对象都需要被Spring所管理,比如提取的通知,和需要加入AOP功能的目标对象
第一步:编写通知(提出的公共功能),交给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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置共性功能的Bean-->
<bean id="advice" class="com.study.aop.AopAdvice"/>
<!--配置AOP:需要导入命名空间-->
<aop:config>
<!--配置切入点:即需要添加AOP功能的方法-->
<aop:pointcut id="pt" expression="execution(* *..*(..))"/>
<!--配置切面,这里需要指定该切面使用的通知,所以需要传入通知的Bean对象-->
<aop:aspect ref="advice">
<!--这里配置通知类型,指定切入点,即要给那个Bean对象的那个方法加入共性功能-->
<aop:before method="function" pointcut-ref="pt" />
</aop:aspect>
</aop:config>
</beans>
十五、AOP配置详解(XML)
15.1aspectJ
aspect是切面的意思,是描述切点和通知之间的关系的,是AOP中的一个概念,而aspectJ是基于Java语言对这个概念的具体实现,所以我们在使用AOP的时候需要导入AspectJ的jar包
15.2aopconfig标签
15.3aopponitcut标签
这是配置切入点,即给那些Bean对象的那些方法加入通知,这个标签也可以写多个
15.4、aopaspect配置
15.5、切入点表达式
切入点表示要需要如AOP同性功能的方法
切入点表达式:是一个快速匹配方法描述的通配格式,类似于正则表达式,即查找切入点的规则




书写技巧,从四个占位符角度开始写,第一个是方法的权限修饰符,第二个是方法的返回值类型,第三个是方法所在的包名类名描述,第四个是方法参数列表,第一位和第二位就是具体的权限修饰符或者返回值,或者就是*,第三位开始如果有..出现后面就开始加一下结构,比如com..后面加的就是类的描述,因为..表示任意通配
其他的使用范例
@within(com.cxh.study.aop.controller.UserAccessAnnotation)
表示拦截含有com.cxh.study.aop.controller.UserAccessAnnotation这个注解的类中所有方法
@annotation(com.cxh.study.aop.controller.UserAccessAnnotation)
表示拦截含有这个注解的方法
例如:
<aop:config>
<!-- 配置公共切入点:下面的切面配置都能用-->
<aop:pointcut id="pt" expression="@within(com.study.annotation.MyAnnotation)"/>
<!-- 局部切入点,-->
<aop:aspect ref="advice">
<aop:before method="function4" pointcut-ref="pt"/>
<!-- 内置切入点,-->
<aop:before method="function4" pointcut="@within(com.study.annotation.MyAnnotation)"/>
</aop:aspect>
</aop:config>
15.6、切入点配置方式
15.6、通知类型

<!--配置AOP-->
<aop:config>
<!--配置切点-->
<aop:pointcut id="pt" expression="execution(* * ..*(..))"/>
<!--配置切面-->
<aop:aspect ref="advice">
<!-- 前置通知-->
<aop:before method="function4" pointcut-ref="pt"/>
<!-- 后置通知-->
<aop:after method="function4" pointcut-ref="pt"/>
<!-- 后置返回通知-->
<aop:after-returning method="function4" pointcut-ref="pt"/>
<!-- 异常通知-->
<aop:after-throwing method="function4" pointcut-ref="pt"/>
<!-- 环绕通知-->
<aop:around method="function4" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
注意这个环绕通知很特殊,需要在通知对原始方法进行处理,即原始方式,怎么执行和要不要执行
所以我们要在环绕通知里进行处理,对原始方法进行调用,根据不同情况处理,可以实现其他四个通知的功能
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("功能一");
//这个proceed()方法上面的都是在原方法执行前进行执行的
//proceed()这个方法表示调用原方法,如果不加,那么原方法就不会执行
//执行执行增强的功能
pjp.proceed();
//下面的表示在原方法执行后的方法
System.out.println("功能二");
}
十六、AOP配置(注解格式)
16.1:AOP中的常用注解
我们要想使用注解开发AOP,需要在Spring核心配置文件中加入开启配置
我们也可以使用注解方式开启AOP的支持
@EnableAspectJAutoProxy
1.通知交给Spring管理:使用@Componnent,@Controller等
2.配置通知类,这个类里定义的方法都是通知方法使用:@Aspect。
3.配置切入点,切入点需要一个切面表达式,所以可以定义一个方法,方法上加入切面表达式,方法内什么都不写,无参无返回值,方法上加@Pointcut(“execution( ..*(..))”)注解,来声明这是一个切入点,方法名就是切入点的Id
@Pointcut("execution(* * ..*(..))")
public void pt(){
}
4.配置切面:
@Before(“切入点ID,即我们定义切入定的方法名,括号不能丢”),下面的配置都是类似的
@After(“pt()”)
@AfterReturning(“pt()”)
@AfterThrowing(“pt()”)
@Around(“pt()”)
这些注解加入到,我们配置的通知方法上面
注意配置切点我们可以单独创建一类,里面提供一系列切点方法,注意这个类不需要交给Spring控制,然后在配置通知的时候使用类名.方法名的方式引入例如:@Around(“类名.pt()”)
16.2AOP注解开发通知执行顺序

16.3纯注解开发Spring开发使用AOP
在注解的核心配置类上加@EnableAspectJAutoProxy,就能开启AOP
16.4:AOP的底层原理
在不改变原有代码的情况下对原有功能进行增强的设计模式:
1.装饰者设计模式
2.JDK动态接口
3.CGLIB动态代理
CGLIB动态代理(Code Generation library):code生成类库,它不限定接口,是根据类进行代理的,他不需要原始的对象,可以动态的创建出新的代理对象,可以根据要代理的类,动态的创建字节码文件在内存中,也就是说它会根据拿的字节码文件进行继承然后在内存中创建一个新的字节码文件
实现方式:
package com.study.proxy;
import com.study.service.UserService;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class UserServiceProxy {
public static UserService getUserService(Class clazz){
//创建这个类的对象就可以在内存中创建一个字节码文件
Enhancer enhancer = new Enhancer();
//调用这个方法来实现继承父类
enhancer.setSuperclass(clazz);
//对原始方法进行调用
enhancer.setCallback(new MethodInterceptor() {
//o产生的代理对象,method是原始对象的方法对象,objects表示原始对象的参数,methodProxy表示带方法对象
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//这里是对原始方法进行调用,注意这里是对原始方法的所有方法都会进行调用
//所以我们需要在这里进行判断
if( method.getName().equals("save")){
Object rt = methodProxy.invokeSuper(o, objects);
System.out.println("增强功能");
return rt;
}
Object rt = methodProxy.invokeSuper(o, objects);
return rt;
}
});
//调用create()方法来创建字节码文件的对象
return (UserService) enhancer.create();
}
}
注意:对于Spring的AOp默认是使用JDK动态代理,如果填true那么就是使用CGLIB动态代理




