学习笔记:Spring框架IOC和AOP

内容较多,建议根据目录梳理食用

Spring版本

版本区别

  1. GA:General Availability,正式发布版,最稳定版本。

  2. PRE:release,预览版,内部测试版。

  3. SNAPSHOT:快照版,可以使用,但仍在继续改进。

  4. ALPHA:内测版。

  5. BETA:公测版。

  6. RELEASE:正式版。

  7. CURRENT:当前推荐的版本。

版本标号

主版本号.子版本号.修正版本号

基础知识

框架构图

Spring框架图

ApplicationContext常用的的实现类

new FileSystemXmlApplicationContext("");
new ClassPathXmlApplicationContext("");

使用第一个实现类加载配置文件要输入xml文件全路径

使用第二个实现类就是当前类路径

IOC

  1. IOC思想是基于IOC容器完成,IOC容器底层就是对象工厂。

  2. Spring提供的IOC容器两种方式:(主要就是两个接口)

    第一种是BeanFactory:是Spring内自己使用的接口,加载配置文件的时候不会去创建对象,只有在获取的时候才会去创建。

    第二种是ApplicationContext:BeanFactory的子接口。

FactoryBean

Spring有两种Bean,第一种是自己常见的Bean,另外一种是工厂Bean(FactoryBean)。即:普通Bean定义的class全类名和返回类的类型相同,工厂Bean返回可以不相同。

建立工厂Bean需要该类实现FactoryBean接口:

import org.springframework.beans.factory.FactoryBean;

//在接口上加上泛型就可以实现返回指定类型对象
public class MyFactoryBean implements FactoryBean {
    
    
    @Override
    public boolean isSingleton() {
    
     return FactoryBean.super.isSingleton(); }

    @Override
    public Object getObject() throws Exception {
    
     return null; }

    @Override
    public Class<?> getObjectType() {
    
     return null; }
}

Bean的生命周期

  1. 创建Bean,即构造器。
  2. 设置Bean的属性和对其他Bean的引用,如:set方法。
  3. 调用Bean的初始化的方法,需要配置。
  4. Bean可以被使用。
  5. 当容器在关闭时,调用销毁Bean的方法,需要配置。

加入Bean的后置处理器后,声明周期增加了两步骤:

  1. 初始化前,把Bean实例传给后置处理器的postProcessBeforeInitialization方法。
  2. 初始化后,把Bean实例传给后置处理器的postProcessAfterInitialization方法。

后置处理器的创建方式:

public class MyBeanPost implements BeanPostProcessor {
    
    }
//重写接口的两个方法后,在配置文件中添加这个类的Bean,spring可以自动识别

XML方式管理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">
    
</beans>

命名空间

  1. p命名空间

    p命名空间主要是用于简化属性的注入。在Spring的xml文件约束中加入如下约束:

    xmlns:p="http://www.springframework.org/schema/p"
    
    <?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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <bean id="Bean id" class="全类名" p:属性=""></bean>
        
    </beans>
    
  2. util命名空间

    util命名空间主要可以提取集合,方便xml中重复使用该集合。

    xmlns:util="http://www.springframework.org/schema/util"
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        
        <!-- 提取List集合,使用时ref="id"即可 -->
        <util:list id="">
            <value></value>
            <value></value>
            <value></value>
        </util:list>
        
    </beans>
    
  3. context命名空间:常用在包扫描

  4. aop命名空间:生成代理对象

创建Bean

基于无参构造进行创建,有参构造方式参见依赖注入

<bean id="对象标识" class="全类名" scope="prototype" init-method="无括号的初始化方法名" destroy-method="无括号的销毁方法名"></bean>

<!-- scope默认单实例(singleton),scope="prototype"为多实例 -->
<!-- scope值还有:request session 生成的对象会放到相对应作用域里 -->
<!-- 初始化方法自动调用 -->
<!-- 销毁方法使用Application的子类内的close方法,需要手动调用 -->
<!-- name:和id相似,name可以使用特殊符号,是为Struts准备的 -->

注入属性

通过DI(依赖注入)实现

注入属性的方法

  1. set方法注入

    <bean id="对象标识" class="全类名">
        <property name="属性" value="需要注入的值"></property>
    </bean>
    
  2. 有参构造注入

    <bean id="对象标识" class="类全名">
        <constructor-arg name="对象的属性名" value=""></constructor-arg>
    </bean>
    
  3. P命名空间注入(主要可以简化xml配置)

    p命名空间其本质是使用set方法注入,因此bean类还需要无参构造和set方法。

    <!-- 在xml中加入约束后就能直接使用p命名空间 -->
    <bean id="xx" class="**" p:xx="**"></bean>
    

各种属性的注入方式

   <bean id="对象标识" class="全类名">
       
       <!----------- 注入字面量 ----------->
       <property name="属性" value="需要注入的值"></property>
       <property name="属性">
           <value>需要注入的值</value>
       </property>
       
       <!----------- 注入外部Bean ----------->
       <property name="属性" ref="被注入类的id"></property>
       
       <!----------- 注入内部Bean ----------->
       <property name="属性名">
           <!-- Bean结构和beans内相同 -->
           <bean id="可以没有id" class="全类名">
               <property name="属性" value=""></property>
           </bean>
       </property>
       
       <!----------- 级联赋值,需要属性的get方法 ----------->
       <property name="属性.属性" value=""></property>
       
       <!----------- 集合类型 ----------->
       <!-- 数组类型 -->
       <property name="属性">
           <array>
               <value></value>
               <value></value>
           </array>
       </property>
       <!-- List集合 -->
       <property name="属性">
           <list>
               <value></value>
               <!-- 或者 -->
               <ref>Bean对象id</ref>
           </list>
       </property>
       <!-- Map集合 -->
       <property name="属性">
           <map>
               <entry key="" value=""></entry>
               <entry key="" value=""></entry>
           </map>
       </property>
       <!-- Set集合 -->
       <property name="属性">
           <set>
               <value></value>
               <value></value>
           </set>
       </property>
       
       <!----------- 设置值为null ----------->
       <property name="属性名"><null/></property>
       
       <!----------- 特殊字符(左右尖括号) ----------->
       <!----------- 可以使用CDATA,也可以直接转义:&lt;&gt; ----------->
       <property name="属性名">
           <value><![CDATA[*******]]></value>
       </property>
       
   </bean>

自动装配

<bean id="对象标识" class="全类名" autowire=""></bean>

<!-- autowire有两个属性:byName根据属性名自动注入,byType根据属性类型自动注入 -->
<!-- byName需要Bean的id和类名相同 -->
<!-- byType方式中,相同类型的Bean不能定义多个 -->

引入外部文件

常用的地方如:数据库连接配置

引入外部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 http://www.springframework.org/schema/context/spring-context.xsd">
    
    
    <!-- 引入外部文件 -->
    <context:property-placeholder location="classpath:文件名"/>
    
    <!-- 使用 -->
    <bean id="对象标识" class="全类名">
        <property name="属性名" value="${key}"></property>
    </bean>
    
</beans>

注解方式管理Bean

四个注解作用相同,对应层使用对应注解,有利于逻辑清晰。

@Component:Bean管理

@Service:Service层

@Controller:Controller层

@Repository:持久层

使用注解需要引入aop的jar包,之后开启组件扫描:

<!-- 引入context命名空间 -->
<?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:property-placeholder location="classpath:文件名"/>
    
    <!--

注入属性

@AutoWired:根据类型自动注入

@Qualifier:根据属性名称注入

@Resource:可以根据类型和名称注入,但是Spring不推荐使用,因为该注解是javax包内的

@Value:注入字面量

import javax.annotation.Resource;

@Component
public class Demo {
    
    

    //类型自动注入
    @Autowired
    private User user1;

    //名称自动注入,主要可以解决接口实现类的注入
    @Autowired
    @Qualifier(value = "users")
    private User user2;

    //类型注入
    @Resource
    private User user3;

    //名称注入
    @Resource(name = "users")
    private User user4;

    @Value("使用@Value注入str的值")
    private String str;

    public void test(){
    
    
        System.out.println(user1+"1使用@Autowired类型注入");
        System.out.println(user2+"2使用@Qualifier名称注入");
        System.out.println(user3+"3使用@Resource类型注入");
        System.out.println(user4+"4使用@Resource名称注入");
        System.out.println("str = " + str);
    }

}

//名称可以忽略不写,默认首字母小写,重复的类报错修改Bean的id
@Component(value = "users")
class User{
    
    
    @Override
    public String toString() {
    
    return "user";}
}

/* 测试类 */
/* Applica.xml中开启了注解扫描 */
public class Tests {
    
    
    @Test
    public void DemoTest(){
    
    
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Applica.xml");
        Demo demo = applicationContext.getBean("demo",Demo.class);
        demo.test();
    }
}

完全注解开发

使用配置类替换xml文件

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "xyz.qnhj.ioc")
public class SpringConfig {
    
     }

/* 测试类 */
public class Tests {
    
    
    @Test
    public void SpringConfigTest(){
    
    
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        Demo demo = (Demo)context.getBean("demo");
        demo.test();
    }
}

AOP

面向切面编程底层使用了动态代理,动态代理分两种:

  1. 有接口情况,JDK动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class JDKProxy {
          
          
        public static void main(String[] args) {
          
          
            DaoImpl daoImpl = new DaoImpl();
            /* 
            第一个参数:加载代理对象的类加载器
            第二个参数:动态代理类需要实现的接口 Class[]类型
            第三个参数:动态代理执行类,会调用里面的invoke方法去执行
            */
            Dao dao = (Dao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(),new Class[]{
          
          Dao.class},new DaoProxy(daoImpl));
            int add = dao.add(1, 2);
            System.out.println("add = " + add);
            dao.updata("测试文本");
        }
    }
    
    //代理类,即增强部分
    class DaoProxy implements InvocationHandler{
          
          
        private Object object;
        public DaoProxy(Object object) {
          
           this.object = object; }
        //增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          
          
            System.out.println(method.getName()+"方法执行前……");
            Object ref = method.invoke(object,args);
            System.out.println(method.getName()+"方法执行完成……");
            return ref;
        }
    }
    
    //接口
    interface Dao{
          
          
        int add(int a,int b);
        void updata(String id);
    }
    
    //接口的实现类
    class DaoImpl implements Dao{
          
          
        @Override
        public int add(int a, int b) {
          
           System.out.println("add方法执行"); return a+b; }
        @Override
        public void updata(String id) {
          
           System.out.println("删除:" + id+"方法执行"); }
    }
    
  2. 没有接口情况,CGLIB动态代理

AOP中的概念

  1. 连接点:可以被增强的方法

  2. 切入点:实际中被增强的方法

  3. 通知(增强):增强的逻辑部分

    通知的五种类型:

    1. 前置通知:方法前执行
    2. 后置通知:方法后执行
    3. 环绕通知:方法前后执行
    4. 异常通知:方法出现异常后执行
    5. 最终通知:类似finally
  4. 切面:把通知应用到切入点的过程动作

切入点表达式

execution(权限修饰符、返回值类型、类的全路径、方法名称(参数列表)),如:

execution(* com.Demo.add(…))

execution(* com.Demo.*(…))

AspectJ

Spring一般基于AspectJ实现AOP,AspectJ不是Spring的组成部分,是一个独立的AOP框架,一般会把AspectJ和Spring一起使用,进行AOP操作。

基于注解方式

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//增强方法 这里面增强方式五种注解
@Component
@Aspect
public class Demo {
    
    
    @Before(value = "execution(* 包名.User.add(..))")
    public void before(){
    
    
        System.out.println("前置通知");
    }
}
import org.springframework.stereotype.Component;

//被增强方法
@Component
public class User{
    
    
    public void add(){
    
    
        System.out.println("user...do...");
    }
}
<?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="包名"></context:component-scan>

    <!--  生成代理对象  -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


</beans>
//测试类
public class Tests {
    
    
    @Test
    public void springAopOneTest(){
    
    
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("App.xml");
        User user = applicationContext.getBean("user", User.class);
        user.add();
    }
}

基于配置文件(不常用)

public class User {
    
    
    public void doSame(){
    
    
        System.out.println("user方法执行了");
    }
}
public class Demo {
    
    
    public void before(){
    
    
        System.out.println("前置通知");
    }
}
<?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 class="增强类" id="demo"></bean>
    <bean class="被增强类" id="user"></bean>

    <!-- 配置AOP -->
    <aop:config>
        <!-- 定义切入点 -->
        <aop:pointcut id="p" expression="execution(* 包名.User.doSame(..))"/>
        <!-- 定义切面 -->
        <aop:aspect ref="demo">
            <!-- method:增强逻辑所在的方法 pointcut-ref:增强哪一个切入点 -->
            <!-- 其他通知相同 -->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>

</beans>
@Test
public void springAopXmlTest(){
    
    
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("AppAopXml.xml");
    User user = applicationContext.getBean("user",User.class);
    user.doSame();
}

完全注解

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {
    
    "包名"})
@EnableAspectJAutoProxy(exposeProxy = true) //开启自动扫描
public class AopConfig {
    
    
}

/* 测试类 */
public class Tests {
    
    
    @Test
    public void SpringConfigTest(){
    
    
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
        User user = applicationContext.getBean("user",User.class);
        user.doSame();
    }
}

公共切入点

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Demo {
    
    
    
    //抽取切入点
    @Pointcut(value = "execution(* 包名.User.add(..))")
    public void pointDemo(){
    
    
    }

    @Before(value = "pointDemo()")
    public void before(){
    
    
        System.out.println("前置通知");
    }
}

多个增强类

多个增强方法设置先后执行顺序,只需要在增强方法上面添加@Order(数字),数字越小,优先级越高,最高为0。

备注

jar包坐标

<!-- IOC部分 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>


<!-- AOP新增部分 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.6.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

版本

Spring版本:5.2.6.RELEASE

学习视频:https://www.bilibili.com/video/BV1Vf4y127N5

猜你喜欢

转载自blog.csdn.net/qq_39950529/article/details/124973774