前言
Spring核心部分:
IOC:控制反转,把创建对象的过程交给Spring容器进行管理,降低对象直接的耦合度
AOP:面向切面,不修改源代码的基础上进行功能增强,比如权限检查,事务,日志收集等
IOC
Spring底层其实是基于xml解析、反射机制、底层通过对象工厂实现IOC过程
伪代码如下:
/**
* @Description
* @Author Fangchenjiang
* @Date 2021/10/13 11:56
*/
public class MyFactory {
public static Object getObject(){
//1.解析xml配置文件获取classValue
String classValue=classValue; //xml解析
//2.通过反射获取Class
Class<?> cls = Class.forName(classValue);
//3.实例化对象
Object instance = cls.newInstance();
return instance;
}
}
基于xml手动调用IOC过程
//1.加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象实例
User user = (User) applicationContext.getBean("user");
System.out.println(user);
//bean.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">
<!--通过Spring创建对象-->
<bean id="user" class="com.gz.xf.domain.User">
</bean>
</beans>
但是实际在Spring底层IOC中,过程非常复杂,底层实现主要通过BeanFactory
和ApplicationContext
接口;而BeanFactory
是IOC基本实现,用于Spring内部,初始化Spring相关组件,不推荐外界直接调用,ApplicationContext
是BeanFactory
是的子接口,功能更强大,可以直接使用
Bean管理(基于XML)
Spring容器中,Bean管理主要有两步:对象创建和属性注入(DI),比如基于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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--通过无参构造并注入属性-->
<bean id="user" class="com.gz.xf.domain.User">
<property name="age" value="20"/>
<property name="name" value="xf"/>
</bean>
<!--通过有参构造注入-->
<bean id="user" class="com.gz.xf.domain.User">
<constructor-arg name="age" value="11"/>
<constructor-arg name="name" value="xf"/>
</bean>
</beans>
在Spring中,又分为两类Bean:普通Bean和FactoryBean,普通Bean也就是我们需要交给Spring去管理的类或者接口,而FactoryBean是Spring内置的一个接口,通常由普通对象实现此接口作为工厂使用,返回某个新对象,而不是直接返回该普通对象的Bean实例,源码解释:
* Interface to be implemented by objects used within a {@link BeanFactory} which
* are themselves factories for individual objects. If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
/**
* @Description
* @Author Fangchenjiang
* @Date 2021/10/13 14:20
*/
public class Pet implements FactoryBean<User> {
//返回具体的Bean,此时Pet类可看作是工厂
//返回User类的Bean
@Override
public User getObject() throws Exception {
System.out.println("Pet工厂返回Bean");
return new User();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
获取FactoryBean创建的对象
<?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="pet" class="com.gz.xf.domain.Pet"></bean>
</beans>
// 1.加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
// 2.获取对象实例
User user = applicationContext.getBean("pet",User.class);
System.out.println(user);
Bean生命周期
Bean的生命周期可以包括下面过程:
1.加载配置文件,调用无参构造对象
2.属性注入
3.初始化前相关处理(可选,需实现BeanPostProcessor处理器)
4.执行初始化
5.初始化后相关处理(可选,需实现BeanPostProcessor处理器)
6.容器关闭,Bean销毁
- Person对象
/**
* @Description
* @Author Fangchenjiang
* @Date 2021/10/13 16:01
*/
public class PerSon {
private String name;
public PerSon() {
System.out.println("1.创建对象");
}
public void initBean(){
System.out.println("4.初始化Bean");
}
public void destory(){
System.out.println("6.关闭容器,销毁实例");
}
public void setName(String name) {
System.out.println("2.属性注入:"+name);
this.name = name;
}
}
- 可选,实现BeanPostProcessor处理器
/**
* @Description
* @Author Fangchenjiang
* @Date 2021/10/13 16:04
*/
public class PersonProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("3.初始化前处理:"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5.初始化后处理:"+beanName);
return bean;
}
}
- 配置文件bean.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-->
<bean id="person" class="com.gz.xf.domain.PerSon" init-method="initBean" destroy-method="destory">
<property name="name" value="小方"></property>
</bean>
<!--后置处理器-->
<bean id="personProcessor" class="com.gz.xf.domain.PersonProcessor">
</bean>
</beans>
- 生命周期测试
public void testLifeCycle(){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
//获取实例
PerSon person = applicationContext.getBean("person",PerSon.class);
// System.out.println(person.toString());
//关闭容器,Bean销毁
applicationContext.close();
}
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: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:component-scan base-package="com.gz.xf.domain"/>
</beans>
运用@Component,@Service,@Repository注解实现对象创建,注解具体意义不再解释
@Component //将StudentService 对象交付给IOC容器管理
public class StudentService {
@Autowired //属性按类型注入
private StudentDao studentDao;
public void doQuery(){
studentDao.doQuery();
}
}
@Repository
public class StudentDao {
public void doQuery(){
System.out.println("query student");
}
}
并通过Autowird注入Dao对象,实现在Service类执行Dao对象的调用,当然被注入的对象也需要交给IOC容器管理。所以StudentDao 类也被**@Repository**修饰
public void testAnnotation(){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean3.xml");
StudentService studentService = applicationContext.getBean("studentService", StudentService.class);
studentService.doQuery();
}
这样将Dao对象和Service对象进行了解耦,交由Spring容器管理。但是有个问题就是没有完全达到注解化开发,如何脱离xml配置,实现完全注解开发呢?我们可以使用Spring提供的**@Configuration**替换掉xml文件,**Component咦?这里是不是和SpringBoot有点挂钩了呢?
/**
* @Description
* @Author Fangchenjiang
* @Date 2021/10/13 18:42
*/
@Configuration //配置类,替换xml配置
@ComponentScan(basePackages = "com.gz.xf.domain") //指定组件扫描等价与<context:component-scan base-package="com.gz.xf.domain"/>
public class Myconfig {
}
通过配置类调用Bean实例方法
//基于配置类
ApplicationContext applicationContext= new AnnotationConfigApplicationContext(Myconfig.class);
StudentService studentService = applicationContext.getBean("studentService",StudentService.class);
//Bean调用
studentService.doQuery();
AOP
面向切面编程,底层本质是通过动态代理实现功能增强,动态代请点击JDK动态代理,而AOP不是由Spring提出来的,而是其他独立的框架,所以要在原Spring项目中实现AOP,需要增加以下依赖:
<!--AOP-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
在AOP中,有几个重要的概念,这里我用通俗的语言概括一波
切面
实现基础功能增强动作的某个类(被@Aspect注解标记),里面主要包含里切入点,通知,增强逻辑等
切入点
业务类中实际需要增强的某些方法
通知
在指定的切入点处(需要被增强的目标方法)的一些操作,有点类似拦截器的思想,对比理解
@Before
前置通知,目标增强方法执行前进行通知
@After
最终通知,目标增强方法执行完成后通知
AfterReturning
返回通知,目标增强方法正常执行返回后通知
Around
环绕通知,目标增强方法执行前通知和目标方法最终执行完成后通知
AfterThrowing
异常通知,目标增强方法执行若出现异常,则通知
下面以男孩打游戏,对打游戏的方法进行AOP功能的增强
- 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"
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/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"
>
<!--开启组件扫描-->
<context:component-scan base-package="com.gz.xf.domain.aopannotation"/>
<!--开启aop代理-->
<aop:aspectj-autoproxy/>
</beans>
- 业务类
//目标业务类
@Component
public class BoyService {
public void palyGame(){
System.out.println("在打和平精英");
}
}
- 业务类的切面,其中execution是指定对哪些类的方法进行AOP的增强
@Component
@Aspect
public class BoyAspect {
//切入点
@Pointcut("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))")
public void point(){
}
// @Before("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))")
@Before("point()")
public void before(){
System.out.println("男孩准备打游戏");
}
// @After("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))")
@After("point()")
public void after(){
System.out.println("男孩打完游戏");
}
// @AfterReturning(("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))"))
@AfterReturning("point()")
public void AfterReturning(){
System.out.println("男孩打完游戏,方法返回");
}
// @Around((("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))")))
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕之前");
joinPoint.proceed();
System.out.println("环绕之后");
}
// @AfterThrowing((("execution(* com.gz.xf.domain.aopannotation.BoyService.*(..))")))
@AfterThrowing("point()")
public void afterThrowing(){
System.out.println("异常通知");
}
}
- 获取Bean,调用目标方法
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("aop.xml");
BoyService boyService = applicationContext.getBean("boyService", BoyService.class);
boyService.palyGame();
上面是基于xml实现AOP,如果切换到注解方式,只需写个配置类:
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.gz.xf.domain.aopannotation")
public class AopConfig {
}
事务
Spring也对事务有了很好的支持支持事务,主要了解声明方式事务,实现方法有xml配置和注解(重点掌握)
- 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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--开启组件扫描-->
<context:component-scan base-package="com.gz.xf.domain.tx"/>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--jdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置实现事务-->
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="change*"/>
</tx:attributes>
</tx:advice>
<!--事务aop配置-->
<aop:config>
<aop:pointcut id="point" expression="execution(* com.gz.xf.domain.tx.AccountService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="point"/>
</aop:config>
</beans>
- 注解配置,在需要实现事务的类或方法上标注**@Transactional**注解即可
@Configuration
@ComponentScan("com.gz.xf.domain.tx")
@EnableTransactionManagement
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/xf_test?characterEncoding=utf-8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
//JdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//DataSourceTransactionManager
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
Transactional注解
此注解中包含了事务相关的核心概念
//事务的传播行为
Propagation propagation() default Propagation.REQUIRED;
//事务的隔离级别
Isolation isolation() default Isolation.DEFAULT;
//事务超时时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//是否只读
boolean readOnly() default false;
本篇知识点代码仓库地址:https://gitee.com/iamFangcJ/spring_demo