一篇搞定Spring,IOC容器,Bean管理,3.AOP底层原理和实现(收下吧,真的很详细)

1.Spring容器的概念

Spring是一个轻量级的框架,可以解决企业开发的复杂性,让开发效率提升,他核心的两个点是:

1.IOC

IOC:在java中,我们程序员一般是去创建一个对象,那么有个问题就是耦合性太高了,在后期的修改和维护的维修成本比较大,在IOC中,对象的创建依赖注入是由容器负责,而不是由程序员直接控制,他是通过dl的注入方式,来实现对象直接的解耦,提高了代码的灵活性和可维护性

2.AOP

AOP:AOP也被称为面向切面,他可以在不改变原代码对功能进行增强

1.2,Spring入门

首先我们要在pom.xml中添加spring的依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.24</version>
        </dependency>


        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.24</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
<!--            <scope>test</scope>-->
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.23</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>



    </dependencies>

在这里我添加了mysql的依赖,大家也可以先添加上去,在后面我们的案例中会用到,如果下载失败或者网络原因下载中断,或者还不会配置maven的可以参考我之前写的

https://blog.csdn.net/m0_70812512/article/details/139581648?spm=1001.2014.3001.5502

在这里就会有你spring的依赖项

如果说你去创建xml没有这个图标说明你spring依赖没有配置好。

好接下来我们通过几个案例来了解到这个spring注入的方式

1.通过xml实现注入

通过我们去拿取这个对象中的方法时都是去创建他,这个会很繁琐,而且后期维护性也不高

那么我们就可以通过spring将对象交给spring去管理,让spring帮我们去注入

1.set注入

首先看这里,在这里我们创建了一个user类,在这个类中我修饰了一个name,并且为这个name提供了一个set方法,用于注入值,并且提供了一个构造函数,那么他现在是符合springbean注入的条件

现在我通过xml来注入,我们定义了一个bean的标签,并且指定了他所以管理的对象是User,在property中我们指定了User中的属性名name,并且为name赋值

现在我们来测试看看会是什么结果

//        加载自定义的spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("beans2.xml");
// 获取配置文件对象User
        User user = context.getBean("user",User.class);
// 操作user对象
//        System.out.println(user);
        user.add();

在这里我们可以看到,我们告诉了spring是那一个配置文件,那他就会根据你配置文件中所定义的bean去构造这个对象,在通过getbean获取到这个对象(我们给每个bean都设置了id),并且调用其中的方法,这样就不在是我们程序员手动创建对象了,而是交给spring管理,让他帮我们来管理这个对象

<!--    属性注入方式-->
    <bean id="user2" class="com.zking.Utile.User">
        <property name="name" value="谭建涛"></property>
    </bean>

    <!--    构造注入方式-->
    <bean id="user3" class="com.zking.Utile.User">
        <constructor-arg name="name" value="有参构造注入方式"></constructor-arg>
    </bean>

    <!--    p注入方式-->
    <bean id="user4" class="com.zking.Utile.User"  p:name="p注入方式">

    </bean>

当然这里还有一些其他的注入方式大家也可以去试试,在这里我们就不全部展示出来了,他们的意思都一样,就是通过springIOC来管理这个创建对象的过程,我们去拿就行

接下来我们来按照我们经常写代码的一些主要配置来搞几个小案例

 我们看这里,我有一个数据访问层和一个业务逻辑层,在业务逻辑层中构造了数据访问层的这个对象,并且里面提供了一个

User_Sevsiver

方法,在这个方中可以去直接实现我们UserDao中的执行语句

好,现在我们来看xml,我将Userdao和UserServer都交给spring管理,并且设置了一个不同的id

在UserServier中就会去注入这个UserDao(上面id是UserDao),那既然我们已经注入了UserDao在这个set方法中了,那我在去调UserServier中的

User_Sevsiver

方法,他不就是会去执行到UserDao中的方法吗

实现了吧,是吧,其他案例我就不一一写了,基本都都大差不差,其实就是原来我们要直接去创建对象,现在不用了,给spring去管理,我们拿就好了,spring通过dl注入实现IOC控制反转

在说一个吧,spring自动装配

在这里我们将UserDao和UserSever交给spring管理,并且在 后面我们添加了autowire="byName",这个的意思是springIOC中的一个自动装配的一方式,他会扫描ImpUserServer中的属性值,比如说是userdao,那么他就会尝试将userdao注入到usersever这个bean当中,就可以实现spring的自动装配

1.3.通过组件扫描的方式实现

虽然说这种xml声明的方式确实可以实现我们的IOC控制反转,但是这个步骤什么的到处配置还是会有点麻烦,那么spring肯定还有其他的方式来实现这个控制反转,那就是组件扫描

1.什么是组件扫描

我们来看这个张图,我们原来是不是声明告诉spring,我指定那些要注入的东西那我们在根据他这个id去调这个bean,就可以实现这个方法了吧。

那有没有一种方式就是说,我可以给每一个对象中去声明一个标识符,在让spring通过标识符来识别变成一个bean,并且扫描不是指定某一个类来实现,而是去扫描结构下的所以类,当然有,我们来看上面的图片,我们指定开起了组件扫描,扫描com.zking下面的所以类

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

    <!--    开启组件扫描,当前扫描com.zking下的全部组件-->
    <!--    <context:component-scan base-package="com.zking"></context:component-scan>-->
    <!--    是 Spring 的注解驱动配置之一,用于自动扫描指定包下的组件,-->
    <!--    如带有 @Component, @Repository, @Service, @Controller 等注解的类,并将其注册为 Spring 的 Bean。-->

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



</beans>

这里的

 @Component, @Repository, @Service, @Controller 

就是我们今天要说的通过注解的方式实现DL注入

首先我们先来了解这是是什么,首先要知道spring就是通过识别出com.zking下面的所以包含这些注解的来作为spring的bean,那有了这个bean,他就可以通过这个bean去里面拿这个对象去实现这个方法。

@Component:是spring当中的一个注解,他一般是拥有业务逻辑层,你可以理解为他将自己变成了一个spring可以扫描识别到变成一个bean

@Repository:一般用于数据访问层,他就是告诉spring我这个类是用于数据访问的

@Service:一看名字就知道了吧,他其实就是告诉spring他是业务逻辑层

@Controller:这个注解标记的类是一个 Spring MVC 控制器组件,用于处理 HTTP 请求和响应。它扩展了 @Component 注解,通常与 Spring MVC 框架结合使用,用于构建 Web 应用。

他们都可以和@Autowired 或 @Resource 等注解来自动装配 Bean

@Autowired:是spring的自动注解,我们声明的全局变量中

在这里我们创建了一个ImpUser_Dao类,并且将他标记为
@Repository("userDao")spring中的组件,这里这个的意思是为相当于注入对象的name属性,可以理解为id,默认是这个类小写,impUser_Dao,那么在 ImpUserSever_Dao 中,我们设置为自动注入@Autowired,在UserDao userDao = null;userdao要和你那个dao里面的@Repository("userDao")值一致,因为他是通过你这个name的属性值在com.zking下去扫描,去找,找到了就将这个对象注入进去,在最后我们就可以通过
UserSever serv = apl.getBean("impUserSever_Dao",UserSever.class);

去创建这个类并且调用他里面的方法(默认是首字母小写),那么他就会自动的将Userdao注入进去,我们在调里面的方法是也就可以执行到数据访问逻辑层中了

那么这种其实还是依赖了xml,如果不想依赖xml,完全注解开发

package com.zking.config;

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

//标记当前类为配置类
@Configuration
// 开启组件扫描
@ComponentScan({"com.zking"})
//引入外部配置文件
@PropertySource("db.properties")
public class SpringUserConfig {
}

我们可以通过创建一个java类,在类中将他声明为他是配置文件

在通过 

ApplicationContext apl = new AnnotationConfigApplicationContext(SpringUserConfig.class);

作引入,是相同的效果。

3.AOP底层原理和实现

1.AOP是什么

简单来说aop就说在不改变原代码时,对原来的功能增强

AOP也被称为面向切面,切面的意思是为他是由我们定义的切点组成的,可以理解为我现在有两个类,一个userdao,一个Studentdao,他们下面分别有add,update方法,那么他们类里面的方法又可以叫切点,那么我们就可以定义这个切点为一个切面,对我们的功能代码增强。 

2.AOP的底层原理

 就是动态代理,动态代理又可以分为

有接口的情况:JDK动态代理

无接口情况:CGLIB动态代理

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

package com.zking.dao.imp;

import com.zking.dao.UserDao;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository
public class ImpUserDao implements UserDao {

    @Override


    public void add() {
        System.out.println("数据添加");
    }

    @Override
    public void Update() {
        System.out.println("数据修改");
    }
}

创建Userdao接口代理对象

 2.无接口情况:CGLIB动态代理

package com.zking.proxy.CGilb;

public class User {
    public void add(){
        System.out.println("CGLIB方式");
    }
    public static class person extends User{
        public void add(){
            System.out.println("CGLIB开启事务");
            super.add();
            System.out.println("CGLIB事务提交");
        }
    }

}

没有接口,而是直接继承这个类中的方法在方法之前作增强

java动态代理

package com.zking.proxy;

import com.zking.dao.UserDao;

/**
 * 用户Dao代理类,用于在不修改原有UserDao实现的基础上,添加额外的功能(如日志、事务等)。
 */
public class UserDaoproxy2 {

    private UserDao userDao;

    /**
     * 构造函数,传入UserDao实例,代理类将围绕这个实例进行操作。
     * 
     * @param userDao 需要被代理的UserDao实例
     */
    public UserDaoproxy2(UserDao userdao) {
        this.userDao = userdao;
    }

    /**
     * 创建UserDao的代理实例。
     * 
     * @return 返回代理后的UserDao实例
     */
    public UserDao newProxyInstance() {
        System.out.println("代理类");
        // 使用Java反射机制创建代理实例,代理实现类为UserInvoaction
        return (UserDao) java.lang.reflect.Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), new UserInvoaction());
    }

    /**
     * InvocationHandler实现类,用于处理代理对象的方法调用。
     */
    class UserInvoaction implements java.lang.reflect.InvocationHandler {
        /**
         * 当调用代理对象的方法时,实际上会执行此方法。
         * 
         * @param proxy 代理对象
         * @param method 被调用的方法
         * @param args 方法参数
         * @return 方法返回值
         * @throws Throwable 方法执行中抛出的异常
         */
        public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
            System.out.println("前置增强");
            // 调用原始UserDao实例的方法
            Object obj = method.invoke(userDao, args);
            System.out.println("后置增强");
            // 返回方法执行结果
            return obj;
        }
    }
}

4.AOP准备工作

Aspectl介绍

在spring当中一般都在基于Aspectl实现aop操作的,Aspectl不是spring的一部分,而且是一个独立的AOP框架,一般把Aspectl和Spring一起使用,进行AOP操作,因为这样更方便

基于Aspectl进行AOP 操作的方式有两种

1.基于xml配置文件的方式实现

2.基于注解的方式实现

基于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
        ">

    <!--    开启组件扫描,当前扫描com.zking下的全部组件-->
    <!--    <context:component-scan base-package="com.zking"></context:component-scan>-->
    <!--    是 Spring 的注解驱动配置之一,用于自动扫描指定包下的组件,-->
    <!--    如带有 @Component, @Repository, @Service, @Controller 等注解的类,并将其注册为 Spring 的 Bean。-->

    <context:component-scan base-package="com.zking"></context:component-scan>
<!--    <context:component-scan base-package="com.zking">-->
<!--        <context:exclude-filter type="assignable" expression="com.zking.aop.LogAspect"/>-->
<!--    </context:component-scan>-->

<!--    开启aspectl生成代理对象-->
<!--    roxy-target-class="true"代理目标可以是类,默认为接口-->
    <aop:aspectj-autoproxy/>

</beans>

首先我们在xml中开启组件扫描,扫描com.zking下面的所以类,并且开启aspectl生成代理对象

 

现在我有一个UserServerImp的类,类中有这些方法

package com.zking.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.management.DescriptorKey;

//@Aspect : 表明当前类是增强类
@Aspect
@Order(1)
@Component
public class LogAspect {
//    设置切点
//@Pointcut("execution(* com.zking.dao..*.*(..))")
@Pointcut("execution(* com.zking..*.*(..))")
public void pointcut(){

}

    @Before("pointcut()")

    public void before() {
        System.out.println("UserSeverimp前置通知");
    }
    @After("pointcut()")
    public void after(){
        System.out.println("UserSeverimp后置通知");
    }
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("UserSeverimp返回通知");
    }
    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("UserSeverimp异常通知");
    }
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable{
//        System.out.println("UserSeverimp环绕通知");
        joinPoint.proceed();
        System.out.println("UserSeverimp环绕通知");
    }

}

并且又创建了一个类,@Aspect : 表明当前类是增强类,

@Pointcut("execution(* com.zking..*.*(..))")

这个帮忙当前com.zking下面所以类中所以的方法,属性为任意类型,这样就组成了一个切面,切面下面有各种各样方方法,那么在执行程序时就会去扫描,看那些增强类,然后对类实现增强功能,

 

可以看到,本来我应该只是会输出用户添加的,但是因为我们配置了AOP,他就会在我们代码执行之前先执行我们增强类中的方法。好了,今天就到这里啦,累了兄弟们。

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值