SpringIOC和AOP机制的学习

发布时间 2023-12-17 23:53:23作者: zL66

SpringIOC和AOP机制的学习

5、HelloSpring_哔哩哔哩_bilibili

springIOC依赖

code-block

直接导入mvc依赖也可以

          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.7</version>
        </dependency>

image-20230530170157131

SpringIOC理论推导

原来业务实现步骤:

  1. Dao实现Mapper接口

  2. Dao实体类

  3. Service业务接口

  4. serviceImp业务实现类

存在的问题

在我们的业务中,用户的需求可能会影响我们原代码。

img

本质上解决了问题,程序员不用再去管理对象的创建

系统的耦合性大大降低,可以更专注在业务的实现上

这是IoC(控制反转)的原型,反转(理解):主动权交给了用户

//在Service层的实现类(UserServiceImpl)增加一个Set()方法
//利用set动态实现值的注入!
private UserDao userDao;
public void setUserDao(UserDao userDao){
    this.userDao = userDao;
}
  • 之前,程序是主动创建对象!控制权在程序猿手上,已经在代码中写死了的 比如一个类的对象=new 类()

  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象!(主动权在客户手上

  • img

IoC本质

控制反转IoC是一种设计思想,Dl(依赖注入)是实现IoC的一种方法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象的依赖关系完全硬编码在程序中,对象的创建由程序控制,控制反转后将对象的创建转移给第三方。

img

控制反转是一种通过描述(XML或者注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入DI

applicationcontext.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中创建对象,在Spring这些都称为bean
        类型 变量名 = new 类型();
        Holle holle = new Holle();

        bean = 对象(holle)
        id = 变量名(holle)
        class = new的对象(new Holle();)
        property 相当于给对象中的属性设值,让str="Spring"
    -->

    <!--使用Spring来创建,在Spring这些都称之为bean-->
    <bean id="hello" class="com.hy.pojo.Hello">
        <property name="str" value="Spring"></property>
        <!--ref是引用容器中创建好的对象-->
         <!--value是具体的值,是基本数据类型-->
    </bean>

</beans>

类的实例化的核心是用到了反射技术,所以类要有无参构造方法 invoke()实现

属性实例化的核心用set注入,所以必须要有下面的set()方法

  • Hello对象是由Spring创建得

  • Hello对象得属性是由Spring设置得

这个过程就叫做控制反转

控制:谁来控制对象得创建,传统应用程序的对象由程序本身创建,使用Spring之后对象由Spring创建。

反转:程序本身不创建对象,而变成被动得接受对象

依赖注入:就是利用set方法进行注入

IoC是一种编程思想,由主动编程编程被动接受

可以通过 newClassPathXmlApplicationContext去浏览底层源码

IoC创建对象的方式

默认方式:通过无参构造

类的实例化的核心是用到了反射技术,所以类要有无参构造方法 invoke()实现

通过有参构造

下标赋值

index指的是有参构造中参数的下标,下标从0开始

<bean id="user" class="pojo.User">
    <constructor-arg index="0" value="chen"/>
</bean>

类型赋值(不建议使用)当出现两个类型一样的就不好配置了

<bean id="user" class="pojo.User">
    <constructor-arg type="java.lang.String" value="kuang"/>
</bean>

直接通过参数名(掌握)

<!-- 比如参数名是name,则有name="具体值" -->
<bean id="user" class="pojo.User">
    <constructor-arg name="name" value="kuang"></constructor-arg>
</bean>
<!--官方的写法-->
<bean id="user" class="pojo.User">
        <constructor-arg ref="beanone"></constructor-arg>
</bean>
<bean id="beanone" class="pojo.XXX"></bean>

总结:在配置文件加载的时候,容器中管理的对象就已经被初始化了!

Spring比作婚庆公司,在你去访问的时候,里面的"对象"就已经被创建好了,当你准备选择谁的时候相当于getBean()操作获取对象。同时多人访问时,也内存中也只是有一个对象。

在注册bean之后就对象的初始化了类似 new 类名()

Spring配置说明

别名:

<alias name="bean的id名" alias="自己起一个名字">
两个名字使用getBean()都可以实例化对象
</alias>

Bean配置:

<!--id:bean的唯一标识符,也就是相当于我们学的对象名
class:bean对象所对应的会限定名:包名+类型
name:也是别名,而且name可以同时取多个别名 -->
<bean id="user" class="pojo.User" name="u1 u2,u3;u4">
    <property name="name" value="chen"/>
</bean>
<!-- 使用时
    User user2 = (User) context.getBean("u1");
-->

import配置

应用场景:当多个人去开发时,每个人都是用不同的bean容器,当需要合并是可以选择一个主要的bean容器,然后导入,当让也可以是classpathxmlApplicationcontext()扫描多个.xml文件

比如

张三(bean1)

李四(bean2)

王五(bean3)

在applicationContext.xml

<import resource="beans.xm1"/>
<import resource="beans2.xml"/>
<import resource="beans3.xm1"/>

使用的时候,直接使用总的配置就可以了

当这些bean中出现重名的情况时Spring按照在总的XML中的导入顺序来进行创建,后导入的会重写先导入的,最终实例化的对象会是后导入XML中的那个\

DI依赖注入环境

构造器注入

set方法注入

依赖注入:set注入!

  • 依赖:bean对象的创建依赖于容器

  • 注入:bean对象中的所有属性,由容器来注入

  • Student类使用bean来将下面的属性全部进行初始化

import jdk.nashorn.internal.objects.annotations.Property;
import lombok.Data;

import java.util.List;
import java.util.Map;
@Data
public class Student {
     private String name;
     private List<String> list;
     private Map<String,String> map;
     private Property property;
     private Address address;
}
<bean id="address" class="com.zhoulei.Dom.Address">
        <property name="city" value="常德"></property>
        <property name="rural" value="澧县"></property>
    </bean>
    <bean id="student" class="com.zhoulei.Dom.Student">
        <property name="name" value="周磊"></property>
        <property name="address" ref="address"></property>
        <!--数组注入-->
        <property name="str">
            <array>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </array>
        </property>
        <!--list注入-->
        <property name="list" >
            <list>
                <value>1</value>
            </list>
        </property>
        <!--map注入-->
        <property name="map">
            <map>
                <entry key="a" value="1"></entry>
            </map>
        </property>
        <!--property文件注入-->
        <property name="property">
            <props>
                <prop key="crod">aaaa</prop>
            </props>
        </property>
    </bean>

注意:当什么属性的值为null时,需要使用

拓展注入(C或P命名空间)

pojo增加User类

package pojo;
@Data
public class User {
    private String name;
    private int id;
    public User() {
    }
}

注意: beans 里面加上这下面两行

使用p和c命名空间需要导入XML约束

xmlns:p=“http://www.springframework.org/schema/p”

xmlns:c=“http://www.springframework.org/schema/c”

?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:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入/set注入,可以直接注入属性的值-》property-->
    <bean id="user" class="pojo.User" p:name="cxk" p:id="20" >
    </bean>

    <!--c命名空间,通过构造器注入,需要写入有参和无参构造方法-》construct-args-->
    <bean id="user2" class="pojo.User" c:name="cbh" c:id="22"></bean>
</beans>

Bean作用域(scope)

<!--单例模式默认所得到的对象都是一个-->
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="singleton"></bean>
<!--原型模式,每次创建getbean之后都是独立的对象-->
<bean id="user2" class="pojo.User" c:name="cxk" c:age="19" scope="prototype"></bean>

作用域

singleTon:单例 每次创建对象都一样

prototype:多例每次创建的对象都不一样

request:创建的对象在Web一次请求中有效,针对Web项目有效

Session:创建的对象在一次会话中有效 Web

GlobalSession:创建的对象在分布式项目中的全局session中有效

Application:创建的对象在整个项目周期内有效

Bean的自动装配

  • 自动装配是Spring满足bean依赖一种方式

  • Spring会在上下文中自动寻找,并自动给bean装配属性

Spring有三种装配方式

  • 手动XML显示装配

  • javaconfig装配

  • 隐式bean自动装配

自动配置属性:

  • autowrire="byname" byname:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid

缺点也很明显当名字出现不相同的时候就无法实现自动装配了,不够灵活

<bean id="dog" class="com.zhoulei.Dom.dog"> </bean>
  <bean id="cat" class="com.zhoulei.Dom.cat"> </bean>
    <bean id="student" class="com.zhoulei.Dom.Student" autowire="byName">

    </bean>
  • autowrite="bytype" bytype:会自动在容器上下文中查找,和自己对象属性类型相同的beanid;

缺点当出现了多个属性一样的bean之后会直接报错。

<bean id="dog" class="com.zhoulei.Dom.dog"> </bean>
  <bean id="cat" class="com.zhoulei.Dom.cat"> </bean>
    <bean id="student" class="com.zhoulei.Dom.Student" autowire="bytype">

    </bean>

注解实现自动装配

jdk1.5支持注解开发,Spring是jdk2.5

使用注解开发前提:

  • 导入约束

    code-block
    

xmlns:context="http://www.springframework.org/schema/context"

- 导入注解支持

​```xml
<context:annotation-config/>

@Autowrited

@Autowrited会更根据类型和名字区寻找bean默认先通过类型bytype如果找不到bean的话就会按照byname来寻找

注解的作用域

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

可以在属性上面和set上面。

使用Autowrited注解可以不需要set方法

在bean环境复杂的情况下不如同一个类型的bean有多个并且不存在一个beanid和类中的属性名称一样的情况我们可以使用@Qualifier注解来标识我们想指定的bean

@Data
public class Student {
      @Autowired
      @Qualifier("dog1")
      private dog dog;
      private cat cat;
}

@Resource注解也具有自动装配的效果是Java自己支持的自动装配注解效果和Autowrited差不多默认是使用byname然后使用bytype

Spring注解开发

  • 需要导入aop依赖

  • 在applicationContent.xml文件中使用包扫描

    code-block
    

<context:component-scan base-package="com.zhoulei">/context:component-scan

### @Component

可以将注解的类放入到bean仓库中管理

​```java
@Component
@Data
public class Student {
    @Value("dog")
    private String dog;
}

衍生的注解

@Component有几个衍生注解,会按照Web开发中,mvc架构中分层。

  • dao (@Repository)

  • service(@Service)

  • controller(@Controller)

这四个注解的功能是一样的,都是代表将某个类注册到容器中

使用Java来配置XML文件

package com.zhoulei.config;

import com.zhoulei.pojo.user;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration//标明这个类是一个Spring的配置类相当与beans标签,这个标签内部也使用Component表名这个注解的类也会交给spring来管理
@ComponentScan("com.zhoulei.pojo")//配置包扫描,使在该包下面的Component以及衍生注解生效,将注解的类放入到bean中由Spring管理
@Import(Myconfig2.class)//导入其他的配置文件
public class Myconfig {
    @Bean
    //在方法上使用这个注解以后相当与xml文件中的bean
    //其中方法名称相当于是id名称
    //返回值相当于是class:后面的值
    //思考使用这方式来怕配置bean如何来赋值呢?
    //简单属性可以使用注解@Value可以赋值
    public user getUser(){
        return new user();
    }
}
public class Springtest {
    @Test
    public void test(){
        ApplicationContext app = new AnnotationConfigApplicationContext(Myconfig.class);
        user user = app.getBean("getUser", user.class);
        System.out.println(user.getName());
    }
}

没有注意到这个细节

会创建两个相同对象问题的说明:王富贵 (lmlx66.top)

学长大佬总结 - -> @Bean是相当于< bean>标签创建的对象,而我们之前学的@Component是通过Spring自动创建的这个被注解声明的对象,所以这里相当于有两个User对象被创建了。一个是bean标签创建的(@Bean),一个是通过扫描然后使用@Component,Spring自动创建的User对象,所以这里去掉@Bean这些东西,然后开启扫描。之后在User头上用@Component即可达到Spring自动创建User对象了

代理模式

什么要学习静态代理模式?

这个就是AOP的底层逻辑

静态代理

主要角色分为:

  • 真实对象,拥有资源可以分配的对象比如房租

  • 抽象角色,一般是接口用来处理动作的。

  • 代理对象,帮助真实对象处理一些公共的繁琐的动作比如中介需要接待客户以及合同

  • 客户,需要得到自己资源的对象。

好处

可以方便拓展公共动作。分工明确

缺点

增加了代码量

AOP切面逻辑

image-20230607150356008

动态代理

  • 动态代理和静态代理角色一样

  • 动态代理使用动态生成。

  • 动态代理主要分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口--jdk动态代理

    • 基于类:cglib

    • Java字节码

    需要提前了解这两个类 proxy :代理 ,invocationHandler :调用处理程序

package com.zhoulei.dome03;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Proxyinvocation implements InvocationHandler {
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    // Foo f =(Foo) Proxy.newProxyInstance(Foo. Class.GetClassLoader(),
    // new Class<?>[] { Foo.Class },
    // handler);
    // 生成代理类
    public Object getProxy(){
        return  Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //处理代理实例并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(rent, args);
        return result;
    }
}

Spring实现AOP

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

Spring学习

1、在Sping模块使用AOP需要在ApplicationContext.XML需要配置包扫描

<!--包扫描-->
<context:component-scan base-package="com.www"></context:component-scan>
<!--aop扫描接口-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2、使用AOP注解开发时首先需要自己定义以Annotion注解对象

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface invokeLog {
//invokeLog是自己定义的注解对象,主要功能是在需要增强的方法上面加上相应的
//注解。
}

3、接下来建立切面类,主要包括可以使用切面表达式(execution)或者使用@annotion注解,使用@Around("pt()")环绕注解,并且该类还要使用@Component加载该类的Bean对象@Aspect是该类变成一个切面类

@Component
@Aspect
public class Cut {
    @Pointcut("@annotation(invokeLog)")
    public void pint(){
    }
    @Around("pint()")
    public Object printf(ProceedingJoinPoint pjp){
        Object[] args = pjp.getArgs(); //获取目标方法的参数
        String str=(String)(args[0]);//将获取到先参数强转换成为String类型
        System.out.println(str);
        MethodSignature ms = (MethodSignature) pjp.getSignature();
        String name = ms.getName();//返回目标方法的名称
        String type = ms.getDeclaringTypeName();//返回目标方法的所在的类名
        System.out.println(name);
        System.out.println(type);
        Object ret=null;
        try {
            ret = pjp.proceed();//执行目标方法
            System.out.println(ret.getClass().getName());//函数的返回值类型
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("方法被执行,生效");
        return ret;
    }

}

Spring-mybatis

导入MyBatis

<!--//mybatis整合Spring的整合包-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.4</version>
</dependency>
<!--Spring操作数据库-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>

复制学长的笔记王富贵 (lmlx66.top)

<?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">

    <!--DataSource:使用Spring的数帮源替换Mybatis的配置 其他数据源:c3p0、dbcp、druid 
        这使用Spring提供的JDBC: org.springframework.jdbc.datasource 
        使用alibabaDruid连接池
扫描jdbc.proprtties文件,配置jdbc的基础属性
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
配置DruidDataSource属性建立连接池
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="username" value="${jdbc.username}"></property>
</bean>
-->
    <!--data source -->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"
            value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource" />
        <!--绑定 mybatis 配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!--这步也可以省略掉,在mybatis-config.xml文件中配置或者使用包扫描(个人图方便)
         或者
扫描
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="configurer">
    <property name="basePackage" value="com.zhoulei"></property>
</bean>
-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!-- sqlSessionTemplate 就是之前使用的:sqlsession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 只能使用构造器注入sqlSessionFactory 原因:它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

</beans>

Spring声明式事务

jar spring-tx 在JDBC中以及依赖了

声明式事务XML配置

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
 <!--结合aop实现事务织入-->
    <!--配置事务的通知类-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给哪些方法配置事务-->
        <!--新东西:配置事务的传播特性 propagation-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <!-- *号包含上面4个方法:
            <tx:method name="*" propagation="REQUIRED"/> -->
        </tx:attributes>
    </tx:advice>
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txpointcut" expression="execution(* mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
    </aop:config>

声明式事务注解配置

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="txManager"/>

在想要实现事务的方法和类上面使用

@Transactional//设置事务回滚异常(class) //开启事务

@Transactional作用域

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)