Spring核心技术IOC和AOP

前言

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中,过程非常复杂,底层实现主要通过BeanFactoryApplicationContext接口;而BeanFactory是IOC基本实现,用于Spring内部,初始化Spring相关组件,不推荐外界直接调用,ApplicationContextBeanFactory是的子接口,功能更强大,可以直接使用

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();
    }

Spring核心技术IOC和AOP

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();

Spring核心技术IOC和AOP
上面是基于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

上一篇:nginx启动服务提示98: Address already in use错误的解决


下一篇:Spring AOP和Spring IoC 我真的有在学了