Spring基础知识和SpringAOP

本文深入解析Spring框架的核心概念,包括IOC、DI、AOP等,详细介绍Spring环境搭建、对象创建、依赖注入、作用域管理,以及AOP编程实现,是Spring初学者和进阶者的必备指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Spring的概述

  1. 什么是Spring框架?
      Spring就是把每个bean(实体类)bean的关系全部交给第三方容器进行管理,这个容器就是Spring,整个对象的生命周期都是交给Spring进行管理的,Spring的核心是:IOC(控制反转)、DI(依赖注入)、AOP(面向切面编程)。
      DI和IOC的区别:SpringIOC负责创建对象和处理对象依赖关系,DI仅仅负责对象赋值的依赖注入。
      Spring框架是可以解决对象创建以及对象之间依赖关系的一种框架,且可以和其他框架一起使用,如:Spring与Struts, Spring与hibernate(是起到整合(粘合)作用的一个框架)。
  2. Spring提供的一站式解决方案:
    Spring整合/集成任何框架,无非都是一个概念,都是把集成的对象交给Spring容器管理。
    1. Spring Core:spring的核心功能: IOC容器, 解决对象创建及依赖关系
    2. Spring Web:Spring对web模块的支持。
      1. 可以与struts整合,让struts的action创建交给spring
      2. SpringMVC模式(MVC是一种软件架构思想,不是设计模式,设计模式是做重构代码,提高代码复用的。)
    3. Spring DAO:Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】
    4. Spring ORM:spring对orm的支持:
      1. 既可以与hibernate整合【session】
      2. 也可以使用spring对hibernate操作的封装
    5. Spring AOP:切面编程
    6. SpringEE:spring 对javaEE其他模块的支持

二、Spring环境搭建

  1. 在pom.xml文件中添加maven坐标:

    <dependencies>
    	<!-- 引入Spring-AOP等相关Jar -->
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-core</artifactId>
    		<version>5.1.6.RELEASE</version>
    	</dependency>
    
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context</artifactId>
    		<version>3.0.6.RELEASE</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context</artifactId>
    		<version>5.1.6.RELEASE</version>
    	</dependency>
    
    	<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-orm</artifactId>
    		<version>5.1.6.RELEASE</version>
    	</dependency>
    	<dependency>
    		<groupId>org.aspectj</groupId>
    		<artifactId>aspectjrt</artifactId>
    		<version>1.9.3</version>
    	</dependency>
    	<dependency>
    		<groupId>org.aspectj</groupId>
    		<artifactId>aspectjweaver</artifactId>
    		<version>1.9.3</version>
    	</dependency>
    	<dependency>
    		<groupId>cglib</groupId>
    		<artifactId>cglib</artifactId>
    		<version>3.2.10</version>
    	</dependency>
    </dependencies>
    
  2. 创建需要交给Spring管理的注入类UserEntity.java:

    package chauncy.entity;
    
    public class UserEntity {
    	private String name;
    	private Integer age;
    	
    	public UserEntity(){
    		System.out.println("UserEntity无参构造函数被执行");
    	}
    	
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Integer getAge() {
    		return age;
    	}
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    }
    
  3. 创建spring.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:p="http://www.springframework.org/schema/p"
        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">  
        <bean id="userEntity" class="chauncy.entity.UserEntity" />
        <!-- 测试Spring的xml是否允许配置beanId重复 -->
        <bean id="userEntityRepeat" class="chauncy.entity.UserEntity" />
    </beans>
    
  4. 创建测试类SpringTest.java:

    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import chauncy.entity.UserEntity;
    
    public class SpringTest {
    	public static void main(String[] args) {
    		// 1.先加载spring容器
    		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    		System.out.println("Spring容器被加载");
    		// 2.使用bean的id查找对象
    		UserEntity userEntity1 = (UserEntity) applicationContext.getBean("userEntity");
    		UserEntity userEntity2 = (UserEntity) applicationContext.getBean("userEntity");
    		// 判断对象在Spring中是否单例存在,比较两个对象的内存地址是否一样,若一样则证明Spring默认是单例的,线程不安全。
    		System.out.println(userEntity1 == userEntity2);
    	}
    }
    

:Spring配置文件中beanId不允许重复,如果分别使用配置文件和注解方式定义重复的beanId则允许。

三、Spring 加载过程

  1. Spring默认是单例还是多例?
    Spring默认是单例,在容器加载的时候会去初始化创建bean对象。
    通过Springbean的无参构造函数,创建出不同对象,判断不同对象的内存地址是否相同,若相同表示spring默认为单例。
    通过Spring配置文件为bean增加scope=“prototype” 属性,设置该bean为多例方式加载,不会在容器启动的时候创建,在用到时单独创建
    <bean id="userEntity" class="chauncy.entity.UserEntity" scope="prototype"  />
    
  2. Spring作用域:
    1. singleton 作用域:
        当一个bean的 作用域设置为singleton, 那么Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把 一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求和引用都 将返回被缓存的对象实例,这里要注意的是singleton作用域和GOF设计模式中的单例是完全不同的,单例设计模式表示一个ClassLoader中 只有一个class存在,而这里的singleton则表示一个容器对应一个bean,也就是说当一个bean被标识为singleton时 候,spring的IOC容器中只会存在一个该bean。
    2. singleton Prototype(原型):
        prototype作用域部署的bean,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作,对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。不管何种作用域,容器都会调用所有对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。 清除prototype作用域的对象并释放任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被singleton作用域bean占用资源的一种可行方式是,通过使用 bean的后置处理器,该处理器持有要被清除的bean的引用。)
    3. singleton request(web项目会用到,用的不多):
        request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
    4. singleton session(web项目会用到,用的不多):
        session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。

四、SpringIOC 容器

SpringIOC容器,是spring核心内容。
作用: 创建对象 & 处理对象的依赖关系。

  1. IOC容器创建对象的方式:
    1. 调用无参数构造器:
      <!-- SpringIOC创建对象方式1:调用无参构造函数 --> 
      <bean id="userEntity1"  class="chauncy.entity.UserEntity"></bean>
      
    2. 带参数构造器:
      <!-- SpringIOC创建对象方式2:调用有参构造函数 --> 
      <bean id="userEntity2" class="chauncy.entity.UserEntity">
          <constructor-arg name="name" value="ChauncyWang"></constructor-arg>
          <constructor-arg name="age" value="18"></constructor-arg>
      </bean>
      
    3. 工厂创建对象:
      工厂类:
      package chauncy.entity;
      
      public class ObjectFactory {
      	
      	/**
      	 * 
      	 * @methodDesc: 功能描述(实例工厂方法)  
      	 * @author: ChauncyWang
      	 * @param: @return   
      	 * @createTime: 2019年4月22日 上午10:57:29   
      	 * @returnType: UserEntity
      	 */
      	public UserEntity getUserEntity(){
      		System.out.println("ObjectFactory->getUserEntity()");
      		return 	new UserEntity("Chauncy",19);
      	}
      	
      	
      	/**
      	 * 
      	 * @methodDesc: 功能描述(静态工厂方法)  
      	 * @author: ChauncyWang
      	 * @param: @return   
      	 * @createTime: 2019年4月22日 上午10:57:29   
      	 * @returnType: UserEntity
      	 */
      	static public UserEntity getStaticUserEntity(){
      		System.out.println("ObjectFactory->getUserEntity()");
      		return 	new UserEntity("Chauncy",20);
      	}
      }
      
      1. 工厂类,静态方法创建对象:
        <bean id="staticFactoryUserEntity" class="chauncy.entity.ObjectFactory"  factory-method="getStaticUserEntity">
        </bean>
        
      2. 工厂类,非静态方法创建对象:
        <!-- SpringIOC创建对象方式3:工厂创建对象(不太常用):1>实例工厂2>静态工厂 --> 
        <bean id="objectFactory" class="chauncy.entity.ObjectFactory"></bean>
        <bean id="userEntity3" factory-bean="objectFactory" factory-method="getUserEntity">
        </bean>
        
  2. DI依赖注入:
    Spring中,如何给对象的属性赋值? 【DI, 依赖注入】
    1. 通过构造函数:
      <bean id="userEntity2" class="chauncy.entity.UserEntity">
          <constructor-arg name="name" value="ChauncyWang"></constructor-arg>
          <constructor-arg name="age" value="18"></constructor-arg>
      </bean>
      
    2. 通过set方法给属性注入值(使用<property>标签必须要有set方法):
      package chauncy.service;
      
      import chauncy.dao.UserDao;
      
      public class UserService {
      	
      	private UserDao userDao;
      	
      	public void add(){
      		System.out.println("业务逻辑层。。。");
      		userDao.add();
      	}
      
      	public void setUserDao(UserDao userDao) {
      		System.out.println("UserService的setUserDao方法");
      		this.userDao = userDao;
      	}
      }
      
      <!-- 使用set方法DI注入 --> 
      <bean id="userDao" class="chauncy.dao.UserDao"></bean>
      <bean id="userService1" class="chauncy.service.UserService">
      	<property name="userDao" ref="userDao"></property>
      </bean>
      
    3. p名称空间(spring3.0以上版本才支持,用的不多,底层也是使用set方法给属性注入):
      <!-- 使用P名称空间DI注入 -->
      <bean id="userService2" class="chauncy.service.UserService" p:userDao-ref="userDao">
      </bean>
      
    4. 自动装配(了解):
      有些字段不需要注入,也会帮我们注入进去,所以不推荐使用。自动装配的方式有:no(默认设置,没有自动装配)、byName(由属性名自动装配)、byType(由属性数据类型自动装配)、constructor(与byType类似,应用于构造函数)。
      <!-- 自动装配DI注入 -->
      <bean id="userService3" class="chauncy.service.UserService" autowire="byName">
      </bean>
      
    5. 注解:
      SpringBoot中应用较多,SpringBoot简化XML配置,全部使用注解代替,而且是微服务架构模式。
      <!-- 如果要使用注解,首先要开启注解权限,扫描注解 -->
      <context:component-scan base-package="chauncy.*"></context:component-scan>
      
      package chauncy.service;
      
      import javax.annotation.Resource;
      
      import org.springframework.stereotype.Service;
      
      import chauncy.dao.UserDao;
      
      //等同于在XML文件中声明<bean id="userService" class="chauncy.service.UserService"></bean>
      @Service
      public class UserService {
      	
      	/**
      	 * @Resource 默认以属性名称userDao,去找bean对象
      	 */
      	@Resource
      	private UserDao userDao;
      	
      	public void add(){
      		System.out.println("业务逻辑层。。。");
      		userDao.add();
      	}
      
      	public void setUserDao(UserDao userDao) {
      		System.out.println("UserService的setUserDao方法");
      		this.userDao = userDao;
      	}
      }
      
      package chauncy.dao;
      
      import org.springframework.stereotype.Repository;
      
      //标识注入DAO层,默认是以<bean id="userDao" class="chauncy.dao.UserDao"></bean>这种方式配置到Spring容器中
      @Repository
      public class UserDao {
      	
      	public void add() {
      		System.out.println("数据库访问层。。。 flag:"+flag);
      	}
      
      }
      
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      import chauncy.entity.UserEntity;
      import chauncy.service.UserService;
      
      public class SpringTest4 {
      	public static void main(String[] args) {
      		// 1.先加载spring容器
      		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring4.xml");
      		System.out.println("Spring容器被加载");
      		// 2.使用bean的id查找对象
      		UserService userService = (UserService) applicationContext.getBean("userService");
      		userService.add();
      		System.out.println(userService);
      	}
      }
      
      1. @Resource与@Autowired的区别:
        @Autowired是sprig框架自带,默认先以类型查找,再以名称查找(名称需要自定义),@Resource是jdk1.6才有(新创建的maven工程默认是jdk1.5),默认先以名称查找,名称查找不到再以类型进行查找。证明方法:改变bean的id、class,查看是否报错。
      2. 如果XML和注解混合使用,则允许定义相同beanId(单独在xml中定义相同beanId会报错),在实际使用的时候使用的是xml定义的同名bean,证明方法:
        <!-- XML和注解混合使用,定义相同beanId,验证是否会报错,若不报错,XML和注解谁先执行 -->
        <bean id="userDao" class="chauncy.dao.UserDao">
        	<property name="flag" value="true"></property>
        </bean>
        
        package chauncy.dao;
        
        import org.springframework.stereotype.Repository;
        
        //标识注入DAO层,默认是以<bean id="userDao" class="chauncy.dao.UserDao"></bean>这种方式配置到Spring容器中
        @Repository
        public class UserDao {
        	
        	//验证XML和注解混合使用定义重名bean,是否报错,先执行哪个
        	private boolean flag;
        	
        	public void add() {
        		System.out.println("数据库访问层。。。 flag:"+flag);
        	}
        
        	public void setFlag(boolean flag) {
        		System.out.println("This is flag:"+flag);
        		this.flag = flag;
        	}	
        }
        
      3. 创建对象以及处理对象依赖关系,相关的注解:
        @Component 指定把一个对象加入IOC容器
        @Repository 作用同@Component; 在持久层使用
        @Service 作用同@Component; 在业务逻辑层使用
        @Controller 作用同@Component; 在控制层使用
        @Resource 属性注入

五、AOP面向切面编程

  1. 应用场景:权限方面、日志处理、控制方面、事务原理(aop)。注:事务的作用:保证数据一致性。
    springAOP使用cglib动态代理,其底层依托于asm框架(字节码控制)实现。AOP编程的作用就是把代码进行分离。
    模拟事务原理底层的aop实现:
    package chauncy.service;
    
    import org.springframework.stereotype.Component;
    
    @Component
    public class AOPService {
    	
    	
    	//开启事务
    	public void begin(){
    		System.out.println("事务开启。。。");
    	}
    	
    	//提交事务
    	public void commit(){
    		System.out.println("事务提交。。。");
    	}
    	
    }
    
    package chauncy.service;
    
    import javax.annotation.Resource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import chauncy.dao.UserDao;
    
    //等同于在XML文件中声明<bean id="userService" class="chauncy.service.UserService"></bean>
    @Service
    public class UserService {
    	
    	/**
    	 * @Resource 默认以属性名称userDao,去找bean对象
    	 */
    	@Resource
    	private UserDao userDao;
    	
    	@Autowired
    	private AOPService aopService;
    	
    	public void add(){
    		aopService.begin();
    		System.out.println("业务逻辑层。。。");
    		userDao.add();
    		aopService.commit();
    	}
    
    	public void setUserDao(UserDao userDao) {
    		System.out.println("UserService的setUserDao方法");
    		this.userDao = userDao;
    	}
    	
    }
    
  2. AOP编程概述:
    1. Aop:aspect object programming 面向切面编程
        功能: 让关注点代码与业务代码分离。
    2. 关注点:
        重复代码就叫做关注点。
    3. 切面:
        关注点形成的类,就叫切面(类)。
        面向切面编程,就是指:对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入“切面类代码”。
    4. 切入点:
        执行目标对象方法,动态植入切面代码。
        可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。
  3. 注解方式实现AOP编程:
    @Aspect  指定一个类为切面类
    @Pointcut(“execution(* chauncy.service.UserService.add(…))”)  指定切入点表达式
    @Before(“pointCut_()”)  前置通知: 目标方法之前执行
    @After(“pointCut_()”)  后置通知:目标方法之后执行(始终执行)
    @AfterReturning(“pointCut_()”)  返回后通知: 执行方法结束前执行(异常不执行)
    @AfterThrowing(“pointCut_()”)  异常通知: 出现异常时候执行
    @Around(“pointCut_()”)  环绕通知: 环绕目标方法执行
    1. mybatis核心配置文件中开启aop注解
      <!-- 开启aop注解 -->
      <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
      
    2. 定义切面类和关注点
      package chauncy.service;
      
      import org.aspectj.lang.ProceedingJoinPoint;
      import org.aspectj.lang.annotation.After;
      import org.aspectj.lang.annotation.AfterReturning;
      import org.aspectj.lang.annotation.AfterThrowing;
      import org.aspectj.lang.annotation.Around;
      import org.aspectj.lang.annotation.Aspect;
      import org.aspectj.lang.annotation.Before;
      import org.springframework.stereotype.Component;
      
      @Component
      //标识为切面
      @Aspect
      public class AOPService {
      	
      	
      	//关注点,开启事务
      	//前置通知
      	@Before("execution(* chauncy.service.UserService.add(..))")
      	public void begin(){
      		System.out.println("##########使用springAOP前置通知,开启事务。。。");
      	}
      	
      	//提交事务
      	//后置通知
      	@After("execution(* chauncy.service.UserService.add(..))")
      	public void commit(){
      		System.out.println("##########使用springAOP后置通知,提交事务。。。");
      	}
      	
      	//异常通知
      	@AfterThrowing("execution(* chauncy.service.UserService.add(..))")
      	public void error(){
      		System.out.println("##########springAOP异常通知");
      	}
      	
      	
      	//运行通知(无异常时才会执行)
      	@AfterReturning("execution(* chauncy.service.UserService.add(..))")
      	public void run(){
      		System.out.println("##########springAOP运行通知");
      	}
      	
      	//环绕通知
      	@Around("execution(* chauncy.service.UserService.add(..))")
      	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
      		System.out.println("##########springAOP环绕通知-之前");
      		proceedingJoinPoint.proceed();
      		System.out.println("##########springAOP环绕通知-之后");
      	}
      }
      
    3. 切入点的实现
      package chauncy.service;
      
      import javax.annotation.Resource;
      
      import org.aspectj.lang.annotation.Aspect;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import chauncy.dao.UserDao;
      
      //等同于在XML文件中声明<bean id="userService" class="chauncy.service.UserService"></bean>
      @Service
      public class UserService {
      	
      	/**
      	 * @Resource 默认以属性名称userDao,去找bean对象
      	 */
      	@Resource
      	private UserDao userDao;
      	
      	public void add(){
      		//1/0会报错,模拟异常通知使用场景
      		int i=1/0;
      		System.out.println("业务逻辑层。。。");
      		userDao.add();
      	}
      
      	public void setUserDao(UserDao userDao) {
      		System.out.println("UserService的setUserDao方法");
      		this.userDao = userDao;
      	}
      }
      
    4. 测试类的编写
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      import chauncy.entity.UserEntity;
      import chauncy.service.UserService;
      
      public class SpringTest4 {
      	public static void main(String[] args) {
      		// 1.先加载spring容器
      		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring4.xml");
      		System.out.println("Spring容器被加载");
      		// 2.使用bean的id查找对象
      		UserService userService = (UserService) applicationContext.getBean("userService");
      		userService.add();
      		System.out.println(userService);
      	}
      }
      
  4. XML方式实现AOP编程:
    <?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:p="http://www.springframework.org/schema/p"
    	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="chauncy.*"></context:component-scan>
    	<!-- XML和注解混合使用,定义相同beanId,验证是否会报错,若不报错,XML和注解谁先执行 -->
    	<bean id="userDao" class="chauncy.dao.UserDao">
    		<property name="flag" value="true"></property>
    	</bean>
    	
    	<!-- 把切面类加入到容器中 -->
    	<bean id="aopService" class="chauncy.service.AOPService"></bean>
    	<!-- aop的配置 -->
    	<aop:config>
    	   <!-- 配置切入点,如果add替换成*表示UserService下所有方法都会当做切入点 -->
    	   <aop:pointcut expression="execution(* chauncy.service.UserService.add(..))" id="pc"/>
    	   <!-- 配置切面 -->
    	   <aop:aspect ref="aopService">
    	       <aop:before method="begin" pointcut-ref="pc"/>
    	       <aop:after method="commit" pointcut-ref="pc"/>
    	       <aop:after-throwing method="error" pointcut-ref="pc"/>
    	       <aop:after-returning method="run" pointcut-ref="pc"/>
    	       <aop:around method="around" pointcut-ref="pc"/>
    	   </aop:aspect>
    	</aop:config>
    
    </beans>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值