framework学习笔记day07---Spring基础

这篇博客详细介绍了Spring框架中的核心注解,包括@Configuration、@ComponentScan、@Bean、@PropertySource和@Import的用法,以及Spring的纯注解开发步骤。接着深入探讨了AOP(面向切面编程),包括AOP的概念、动态代理(JDKProxy和CGLIB)、AOP的名词解释,以及AOP入门案例。博主总结了注解开发的优缺点,并提供了丰富的代码示例来帮助理解。

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

新注解说明

@configuration

  • 用于指定当前类是一个 spring 配置类;
  • 相当于spring.xml配置文件。

@ComponenScan

  • 用于扫描注解;
  • 相当于<context:component-scan base-package=“xxx”>标签

@Bean

  • 该注解只能写在方法上,表明使用方法创建一个对象,并且放入 spring 容器;
  • 相当于标签

@PropertySource

  • 用于加载properties文件;
  • 相当于<context:property-placeholder location=“xxx.properties”>标签

@Import

  • 用于导入其他配置类;
  • 相当于标签

Spring纯注解开发

  • 开发步骤
    • ①定义service层及其实现子类
    • ②定义dao层及其实现子类
    • ③定义MySpringCoreConfiguration
    • ④定义MySpringDaoConfiguration
    • ⑤代码测试
  • ①定义service层及其实现子类
@Service
public class UserServiceImpl implements UserService {


    @Autowired
    private UserDao userDao;


    @Override
    public List<User> selectUserList() throws Exception {
        return userDao.selectUserList();
    }
}

②定义dao层及其实现子类

@Repository
public class UserDaoImpl implements UserDao {

    @Autowired
    private QueryRunner queryRunner;

    @Override
    public List<User> selectUserList() throws Exception {
        return queryRunner.query(
                "select user_id userId,user_name userName, user_pwd userPwd from tb_user",
                new BeanListHandler<>(User.class)
        );
    }
}

③定义MySpringCoreConfiguration

@Configuration
@ComponentScan("com.atguigu")//扫描注解
@Import(MySpringDaoConfiguration.class)
public class MySpringCoreConfiguration {



}

④定义MySpringDaoConfiguration

@Configuration
@PropertySource("jdbc.properties")
public class MySpringDaoConfiguration {


    @Value("${driverClassName}")
    private String driverClassName ;

    @Value("${url}")
    private String url ;

    @Value("${user}")
    private String user ;

    @Value("${password}")
    private String password ;

    @Bean
    public QueryRunner getQueryRunner(DataSource ds){
        return new QueryRunner(ds);
    }

    @Bean
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        return dataSource;
    }

}

⑤代码测试

public class UserController {


    public static void main(String[] args) throws Exception {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MySpringCoreConfiguration.class);
        UserService userService = applicationContext.getBean(UserService.class);
        List<User> userList = userService.selectUserList();
        System.out.println("userList = " + userList);
    }

}```

```java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MySpringCoreConfiguration.class)
public class UserServiceTest {


    @Autowired
    private UserService userService ;

    @Test
    public void selectUserList() throws Exception {
        List<User> userList = userService.selectUserList();
        System.out.println("userList = " + userList);
    }


}

注解开发的作用和弊端

总结

  • 自定义类使用注解来处理
  • 第三方类使用XML来处理

AOP

概述

  • 概述
    • AOP : aspect oritented programing : 面向切面编程
    • AOP关注的是程序中的共性功能,开发时,将共性功能抽取出来制作成独立的功能模块,此 时原始功能中将不具有这些被抽取出的共性功能代码。在被抽取的共性功能的模块运行时 候,将共性功能模块也运行,即可完成原始的功能。
  • 作用
    • 在程序运行期间,不修改源码对已有方法进行增强。
  • 好处
    • 减少重复代码
    • 提供程序效率
    • 方便项目维护

AOP原理环境搭建

代码实现

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("权限校验");
        System.out.println("UserServiceImpl addUser");
        System.out.println("日志记录");
    }

    @Override
    public void deleteUser() {
        System.out.println("权限校验");
        System.out.println("UserServiceImpl deleteUser");
        System.out.println("日志记录");
    }

    @Override
    public void updateUser() {
        System.out.println("UserServiceImpl updateUser");
    }

    @Override
    public void selectUserList() {
        System.out.println("UserServiceImpl selectUserList");
    }
}

存在问题

  • 将主要功能和辅助功能写到一块,不方便程序维护,增加程序的耦合性;
  • 不满足单一职责原则。

动态代理之JDKProxy

  • 概述
    • 基于接口的代理类实现方案
  • 开发步骤
    • ①定义UserServiceJDKProxy工具类
      • 获取代理类对象
    • ②代码测试
  • ①定义UserServiceJDKProxy工具类

public class UserServiceJDKProxy {


    /**
     * 获取UserService(被代理类)对象的代理类对象
     *
     * @param userService : 被代理类对象
     * @return
     */
    public static UserService createUserServiceJDKProxy(UserService userService) {
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),//被代理类的类加载器
                userService.getClass().getInterfaces(),//被代理类实现的所有接口
                new InvocationHandler() {//执行增强 (执行被代理类的方法)
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //Object proxy: 代理类对象
                        // Method method:被代理类的原始方法
                        // Object[] args:方法的实际参数
                        Object result = null;
                        if ("addUser".equals(method.getName()) || "deleteUser".equals(method.getName())) {
                            System.out.println("权限校验");
                            //执行被代理类的原始方法
                            result = method.invoke(userService, args);
                            System.out.println("日志记录");
                        } else {
                            result = method.invoke(userService, args);
                        }
                        return result;
                    }
                }
        );

        return proxy;
    }


}
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("UserServiceImpl addUser");
    }

    @Override
    public void deleteUser() {
        System.out.println("UserServiceImpl deleteUser");
    }

    @Override
    public void updateUser() {
        System.out.println("UserServiceImpl updateUser");
    }

    @Override
    public void selectUserList() {
        System.out.println("UserServiceImpl selectUserList");
    }
}

②代码测试

public class UserServiceJDKProxyTest {

    @Test
    public void createUserServiceJDKProxy() {
        //被代理类对象
        UserService userService = new UserServiceImpl();
        //获取代理类对象
        UserService userServiceJDKProxy = UserServiceJDKProxy.createUserServiceJDKProxy(userService);
        userServiceJDKProxy.addUser();
        userServiceJDKProxy.deleteUser();
        userServiceJDKProxy.updateUser();
        userServiceJDKProxy.selectUserList();

    }
}

存在问题

  • 必须要有接口!!!

动态代理之CGLIB

  • 概述
    • 不限定是否具有接口,可以对任意操作进行增强
    • 不需要原始被代理对象,动态创建新的代理对象
  • 工作流程
    • image-20211227113428497
  • 开发步骤
    • ①定义UserServiceCGLIBProxy类
      • 获取代理类对象
    • ②代码测试
  • ①定义UserServiceCGLIBProxy类
/**
 * 获取UserService代理类对象
 */
public class UserServiceCGLIBProxy {

    /**
     * 获取代理类对象
     *
     * @param clazz : 被代理类的字节码对象
     * @return
     */
    public static UserService createUserServiceCGLIBProxy(Class clazz) {
        Enhancer enhancer = new Enhancer();
        //被代理类是代理类的父类
        enhancer.setSuperclass(clazz);
        //增强方法
        enhancer.setCallback(new MethodInterceptor() {//拦截被代理类的原始方法
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //Object o: 代理类对象
                // Method method: 被代理类的原始方法
                // Object[] args: 方法的实际参数
                // MethodProxy methodProxy: 代理类的方法

                //执行被代理类对象的原始方法
                //Object result = method.invoke(o, args);//死路一条
                //Object result = methodProxy.invoke(o, args);//死路二条
                Object result = null;
                if ("addUser".equals(method.getName()) || "deleteUser".equals(method.getName())) {
                    System.out.println("权限校验");
                    result = methodProxy.invokeSuper(o, args);//活路一条
                    System.out.println("日志记录");
                } else {
                    result = methodProxy.invokeSuper(o, args);
                }
                return result;
            }
        });
        return (UserService) enhancer.create();
    }

}

②代码测试

public class UserServiceCGLIBProxyTest {

    @Test
    public void createUserServiceCGLIBProxy() {

        UserService userService = UserServiceCGLIBProxy.createUserServiceCGLIBProxy(UserServiceImpl.class);
        userService.addUser();
        userService.deleteUser();
        userService.updateUser();
        userService.selectUserList();

    }
}

AOP名词解释

  • 连接点 : join point
    • 被代理类中的所有方法
  • 切入点: pointcut
    • 被代理类中的具有共性功能的方法
  • 通知: advice
    • 将共性功能抽取成独立的方法
  • 切面:aspect
    • 通知和切入点的关系,比如:前置关系、后置关系、环绕关系等等
  • 目标对象类
    • 包含切入点方法的类
  • 织入
    • 将共性功能(通知)加入到原始方法(切入点)执行的过程

AOP入门案例

  • 开发步骤
    • ①引入相关依赖
      • spring-core、spring-beans、spring-context、spring-expression、spring-jcl、spring-aop、spring-aspects等等
    • ②制作目标对象类
    • ③制作通知类
    • ④编写spring-core.xml
      • 配置切面
    • ⑤代码测试
  • ①引入相关依赖
<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <junit.version>4.13.2</junit.version>
    <lombok.version>1.18.22</lombok.version>
    <spring.version>5.3.13</spring.version>
    <dbutils.version>1.7</dbutils.version>
    <druid.version>1.2.8</druid.version>
    <mysql.version>5.1.48</mysql.version>
</properties>

<dependencies>

    <!--junit start-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <!--junit end-->

    <!--lombok start-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </dependency>
    <!--lombok end-->

    <!--spring start-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jcl</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>

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

    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <!--spring end-->


    <!--jdbc start-->
    <dependency>
        <groupId>commons-dbutils</groupId>
        <artifactId>commons-dbutils</artifactId>
        <version>${dbutils.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>
    <!--jdbc end-->


</dependencies>

②制作目标对象类

public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {//切入点
        System.out.println("UserServiceImpl addUser");
    }

    @Override
    public void deleteUser() {//切入点
        System.out.println("UserServiceImpl deleteUser");
    }

    @Override
    public void updateUser() {//连接点
        System.out.println("UserServiceImpl updateUser");
    }

    @Override
    public void selectUserList() {//连接点
        System.out.println("UserServiceImpl selectUserList");
    }
}

③制作通知类

/**
 * 通知类
 */
@Component
public class MyAdvice01 {

    /**
     * 通知
     */
    public void checkPermission(){
        System.out.println("权限校验");
    }

    /**
     * 通知
     */
    public void printLog(){
        System.out.println("日志记录");
    }

}

④编写spring-core.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 https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--扫描注解-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>

    <!--配置切面 : 通知和切入点关系-->
    <aop:config>
        <aop:aspect ref="myAdvice01">
            <!--前置通知-->
            <aop:before method="checkPermission" pointcut="execution(public void com.atguigu.service.impl.UserServiceImpl.addUser())"></aop:before>
            <!--后置通知-->
            <aop:after method="printLog" pointcut="execution(public void com.atguigu.service.impl.UserServiceImpl.addUser())"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

⑤代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-core.xml")
public class UserServiceImplTest {

    @Autowired
    private UserService userService;

    @Test
    public void addUser() {
        userService.addUser();
    }

    @Test
    public void deleteUser() {
        userService.deleteUser();
    }

    @Test
    public void updateUser() {
        userService.updateUser();
    }

    @Test
    public void selectUserList() {
        userService.selectUserList();

    }
}

切入点表达式

语法

execution(权限修饰符 返回值类型 包名.类名.方法名(形参类型1,形参类型2...))

execution(public void com.atguigu.service.impl.UserServiceImpl.addUser(int))

-- 权限修饰符可以省略
execution( void com.atguigu.service.impl.UserServiceImpl.addUser(int))

-- 不限定返回值类型
execution( * com.atguigu.service.impl.UserServiceImpl.addUser(int))

-- 不限定包
execution( * *..UserServiceImpl.addUser(int))

-- 不限定类
execution( * *..*ServiceImpl.addUser(int))

-- 不限定方法名
execution( * *..*ServiceImpl.*(int))

-- 不限定方法参数
execution( * *..*ServiceImpl.*(..))
    • 权限修饰符可以省略
    • '*'代表任意包名、任意类名、任意方法名、任意参数类型
    • '.'代表任意一个包下
    • '…'代表任意一个或多个包、任意一个或多个参数(类型任意)

切入点表达式抽取

代码实现

<aop:config>
    <aop:aspect ref="myAdvice01">
        <aop:pointcut id="p1" expression="execution( * *..*ServiceImpl.*(..))"/>
        <!--前置通知-->
        <aop:before method="checkPermission" pointcut-ref="p1"></aop:before>
        <!--后置通知-->
        <aop:after method="printLog" pointcut-ref="p1"></aop:after>
    </aop:aspect>
</aop:config>
<aop:config>
    <aop:pointcut id="p1" expression="execution( * *..*ServiceImpl.*(..))"/>
    <aop:aspect ref="myAdvice01">
        <!--后置通知-->
        <aop:after method="printLog" pointcut-ref="p1"></aop:after>
    </aop:aspect>
    <aop:aspect ref="myAdvice01">
        <!--前置通知-->
        <aop:before method="checkPermission" pointcut-ref="p1"></aop:before>
        <!--后置通知-->
    </aop:aspect>
</aop:config>
<aop:config>
    <aop:aspect ref="myAdvice01">
        <!--前置通知-->
        <aop:before method="checkPermission"
                    pointcut="execution( * *..*ServiceImpl.*(..))"></aop:before>
        <!--后置通知-->
        <aop:after method="printLog"
                   pointcut="execution( * *..*ServiceImpl.*(..))"></aop:after>
    </aop:aspect>
</aop:config>

通知类别

通知类别

通知类别说明
before前置通知,在切入点之前执行
after-returning正常后置通知,在切入点之后执行,切入点没有异常才执行
after-throwing异常后置通知,在切入点之后执行,切入点有异常才执行
after后置通知,在切入点之后执行,切入点不管是否有异常都执行
around环绕通知,在切入点之前执行,在切入点之后执行

代码实现

/**
 * 通知类别
 */
@Component
public class MyAdvice02 {


    /**
     * 前置通知  ok
     */
    public void before(){
        System.out.println("MyAdvice02 before");
    }

    /**
     * 正常后置通知
     */
    public void afterReturning(){
        System.out.println("MyAdvice02 afterReturning");
    }


    /**
     * 异常后置通知 ok
     */
    public void afterThrowing(){
        System.out.println("MyAdvice02 afterThrowing");
    }


    /**
     * 后置通知 ok
     */
    public void after(){
        System.out.println("MyAdvice02 after");
    }


    /**
     * 环绕通知 ok
     */
    public void around(ProceedingJoinPoint pjp){
        System.out.println("MyAdvice02 around之前");

        //执行切入点
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        System.out.println("MyAdvice02 around之后");

    }

}
<aop:config>
    
    <aop:aspect ref="myAdvice02">
        <aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>

        <aop:before method="before" pointcut-ref="p1"></aop:before>

        <aop:after-returning method="afterReturning" pointcut-ref="p1"></aop:after-returning>

        <aop:after-throwing method="afterThrowing" pointcut-ref="p1"></aop:after-throwing>

        <aop:after method="after" pointcut-ref="p1"></aop:after>

        <aop:around method="around" pointcut-ref="p1"></aop:around>
    </aop:aspect>
    
</aop:config>

around通知模拟其他通知

  • 代码实现
@Component
public class MyAdvice03 {

    /**
     * 前置通知
     */
    public void around2Before(ProceedingJoinPoint pjp){
        System.out.println("MyAdvice03 before");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }


    /**
     * 正常后置通知
     * @param pjp
     */
    public void around2AfterReturning(ProceedingJoinPoint pjp){
        try {
            pjp.proceed();
            //1
            System.out.println("MyAdvice03 afterReturning");
        } catch (Throwable e) {
            e.printStackTrace();
            //2
        }
        //3

    }


    /**
     * 异常后置通知
     * @param pjp
     */
    public void around2AfterThrowing(ProceedingJoinPoint pjp){
        try {
            pjp.proceed();
            //1
        } catch (Throwable e) {
            e.printStackTrace();
            //2
            System.out.println("MyAdvice03 afterThrowing");
            throw new RuntimeException(e);
        }
        //3

    }


    /**
     * 后置通知
     * @param pjp
     */
    public void around2After(ProceedingJoinPoint pjp){
        try {
            pjp.proceed();
            //1
        } catch (Throwable e) {
            e.printStackTrace();
            //2
            throw new RuntimeException(e);
        } finally {
            //3
            System.out.println("MyAdvice03 after");
        }

    }

}

通知获取切入点输入参数

  • 分类
    • ①around通知获取切入点输入参数
    • ②非around通知获取切入点输入参数
  • 代码实现
@Component
public class MyAdvice04 {


    /**
     * ①around通知获取切入点输入参数
     */
    public void around(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }



    /**
     * ②非around通知获取切入点输入参数
     * @param jp
     */
    public void before(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));

    }
}

通知获取切入点的返回值

  • 概述
    • before : 无法获取
    • after-returning : 可以获取
    • after-throwing : 无法获取
    • after : 无法获取
    • around : 可以获取
  • 代码实现
@Component
public class MyAdvice05 {


    public void afterReturning(String result){
        System.out.println("MyAdvice05 afterReturning result : " + result);
    }

    public Object around(ProceedingJoinPoint pjp){
        System.out.println("MyAdvice05 around之前");
        try {
            Object result = pjp.proceed();
            System.out.println("MyAdvice05 around result = " + result);
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("MyAdvice05 around之后");
        return null;
    }

}
<aop:config>
    <aop:aspect ref="myAdvice05">
        <aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>

        <aop:around method="around" pointcut-ref="p1"></aop:around>
        <aop:after-returning method="afterReturning" pointcut-ref="p1" returning="result"></aop:after-returning>

    </aop:aspect>

</aop:config>

通知获取切入点的异常信息

  • 概述
    • before : 无法获取
    • after-returning : 无法获取
    • after-throwing : 可以获取
    • after : 无法获取
    • around : 可以获取
  • 代码实现
@Component
public class MyAdvice06 {


    public void afterThrowing(Exception e){
        System.out.println("MyAdvice05 afterThrowing exception : " + e);
    }

    public void around(ProceedingJoinPoint pjp){
        System.out.println("MyAdvice06 around之前");
        try {
            pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("MyAdvice06 around exception : " + e);
            throw new RuntimeException(e);
        }
        System.out.println("MyAdvice06 around之后");

    }



}
<aop:config>
    <aop:aspect ref="myAdvice06">
        <aop:pointcut id="p1" expression="execution(* *..*Service.*(..))"/>

        <aop:around method="around" pointcut-ref="p1"></aop:around>
        <aop:after-throwing method="afterThrowing" pointcut-ref="p1" throwing="e"></aop:after-throwing>
    </aop:aspect>

</aop:config>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值