Java学习笔记 Spring

Spring

IOC

Bean

  1. Bean的种类

普通bean:Spring直接创建实例并返回

工厂bean:具有工厂生成对象的能力,通过getObject()方法生成对象。


  1. Bean的生命周期
  • Bean的生命周期分为Bean的定义、Bean的初始化、Bean的生存期、Bean的销毁4个部分。

Bean的定义:Spring通过配置扫描进行资源定位,定位到资源后对资源进行解析并将定义的信息进行保存,然后将Bean定义存入IOC容器中。

Bean的初始化:创建Bean的实例对象,进行依赖注入。依赖注入后为Bean实例设置属性,将Bean实例传递给后置处理器postProcessBeforeInitialization方法,调用Bean的初始化方法(通过@PostConstruct标注方法),将Bean实例传递给后置处理器postProcessAfterInitialization方法。

Bean的生存期

Bean的销毁:调用销毁方法(通过@PreDestroy定义)销毁Bean。


  1. bean的作用域

singleton:单例类型,在创建容器时就会创建该bean对象,每次获取时返回的都是同一个对象。(默认值)

prototype:每次获取时都返回一个新的实例。

request:每次http请求都会创建一个bean

session:同一个http session共享一个bean,不同的session用不同的bean

application:全局作用域,应用在整个web范围的实例。

globalSession:在全局HTTP Session中,一个Bean定义对应一个实例。实践过程中基本不使用。


AOP

AOP术语

  • 连接点(join point):被拦截的对象,即方法。
  • 切点(point cut):提供使切面适配一个连接点的功能,它能够向Spring描述那些方法需要启用AOP编程。
  • 通知(advice):按照约定流程下的方法分为前置通知、后置通知、环绕通知、事后返回通知、异常通知。它会根据约定织入流程中。
  • 目标对象(target):被代理对象。
  • 引入(introduction):引入新的类和其方法用于加强现有Bean对象。
  • 织入(weaving):通过动态代理,对与切点匹配的连接点进行拦截,并按照约定将各类通知织入流程中。
  • 切面(aspect):定义切点、各类通知和引入的内容,用于增强Bean对象或将对应方法织入流程。

动态代理实现

定义拦截器接口:

package com.example.aopdemo.interceptor;

import com.example.aopdemo.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

/**
 * 拦截器接口
 */
public interface Interceptor {
    // 事前方法
    public boolean before();

    // 事后方法
    public void after();

    /**
     * 取代原有事件的方法
     * @param invocation 回调原有事件的参数
     * @return 原有事件返回对象
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException;

    // 是否返回方法。事件没有发生异常执行
    public void afterReturning();

    // 事后异常方法。事件发生异常后执行
    public void afterThrowing();

    // 是否使用around方法取代原有方法
    boolean useAround();

}

实现拦截器:

package com.example.aopdemo.interceptor;

import com.example.aopdemo.invoke.Invocation;

import java.lang.reflect.InvocationTargetException;

public class MyInterceptor implements Interceptor{

    // 事前方法
    @Override
    public void before() {
        System.out.println("befor ......");
    }

    // 事后方法
    @Override
    public void after() {
        System.out.println("after ......");
    }

    // 取代原有事件的方法
    @Override
    public Object around(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
        System.out.println("around befor ......");
        Object obj = invocation.proceed();
        System.out.println("around after ......");
        return obj;
    }

    // 事后返回方法
    @Override
    public void afterReturning() {
        System.out.println("afterReturning ......");
    }

    // 事后异常方法
    @Override
    public void afterThrowing() {
        System.out.println("afterThrowing ......");
    }

    // 是否使用around方法取代原有方法
    @Override
    public boolean useAround() {
        return true;
    }
}

Invocation类:

package com.example.aopdemo.invoke;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Invocation {
    private Object[] params;
    private Method method;
    private Object target;

    public Invocation(Object target, Method method, Object[] params) {
        this.target = target;
        this.method = method;
        this.params = params;
    }

    // 通过反射调用原方法
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return method.invoke(target, params);
    }
}

代理:

package com.example.aopdemo.proxy;

import com.example.aopdemo.interceptor.Interceptor;
import com.example.aopdemo.invoke.Invocation;

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

public class ProxyBean implements InvocationHandler {
    private Object target = null;

    private Interceptor interceptor = null;

    /**
     * 绑定代理对象
     * @param target 被代理对象
     * @param interceptor 拦截器
     * @return 代理对象
     */
    public static Object getProxyBean(Object target, Interceptor interceptor) {
        ProxyBean proxyBean = new ProxyBean();
        // 保存被代理对象
        proxyBean.target = target;
        // 保存拦截器
        proxyBean.interceptor = interceptor;
        // 生成代理对象
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), proxyBean);

        return proxy;
    }

    /**
     * 处理代理对象方法
     * @param proxy 代理对象
     * @param method 方法
     * @param args 运行参数
     * @return 方法调用结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 异常标识
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, args);
        Object retObj = null;
        // 执行事前方法
        this.interceptor.before();
        try {
            if(this.interceptor.useAround()) {
                // 如果需要使用around方法覆盖原方法,则执行around方法
                retObj = this.interceptor.around(invocation);
            } else {
                // 否则执行原方法
                retObj = method.invoke(target, args);
            }
        } catch (Exception e) {
            // 如果发生异常则进行标记
            exceptionFlag = true;
        }
        // 执行事后方法
        this.interceptor.after();
        if(exceptionFlag) {
            // 如果发生了异常则执行事后异常方法
            this.interceptor.afterThrowing();
        } else {
            // 没发生异常则执行事后返回方法,并返回方法调用结果。
            this.interceptor.afterReturning();
            return retObj;
        }
        return null;
    }
}

编写service接口和impl实现类,通过以下代码测试:

HelloService helloService = new HelloServiceImpl();
HelloService proxy = (HelloService) ProxyBean.getProxyBean(helloService, new MyInterceptor());
proxy.sayHello(new User("Bob", "111", 99));

得到结果:

befor ......
around befor ......
User(name=Bob, id=111, age=99)hello!
around after ......
after ......
afterReturning ......

环绕通知

切面:

package com.example.aopdemo.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * 切面
 */
@Aspect
public class MyAspect {

    // 切点
    @Pointcut("execution(* com.example.aopdemo.service.impl.UserServiceImpl.printUser(..))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void before() {
        System.out.println("before ...");
    }

    @After("pointCut()")
    public void after() {
        System.out.println("after ...");
    }

    @Around("pointCut()")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("around before ...");
        // 回调原方法
        jp.proceed();
        System.out.println("around after ...");
    }

    @AfterReturning("pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning ...");
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing ...");
    }
}

Controller类实现:

package com.example.aopdemo.controller;

import com.example.aopdemo.pojo.User;
import com.example.aopdemo.service.HelloService;
import com.example.aopdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/print")
    @ResponseBody
    public User printUser(String id, String name, int age) {
        User user = new User();
        user.setId(id);
        user.setName(name);
        user.setAge(age);

        userService.printUser(user);
        return user;
    }
}

在Application类中定义切面:

    // 定义切面
    @Bean(name = "myAspect")
    public MyAspect initMyAspect() {
        return new MyAspect();
    }

启动服务器,在浏览器输入http://localhost:8080/user/print?id=111&name=Bob&age=66,得到

{"name":"Bob","id":"111","age":66}

控制台打印:

around before ...
before ...
User(name=Bob, id=111, age=66)
afterReturning ...
after ...
around after ...

全局异常处理

全局异常处理类可以处理Controller抛出的所有异常,在其他曾遇到异常不需要处理,抛出到controller层即可。

package com.example.demo.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常处理
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 异常处理方法
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public String exceptionHandler(SQLIntegrityConstraintViolationException e) {
        log.error(e.getMessage());

        //违反唯一约束
        if(e.getMessage().contains("Duplicate entry")) {
            String[] split = e.getMessage().split(" ");
            String msg = split[2] + " 已存在";
            return msg;
        }
        return "未知错误";
    }

}

Spring事务

事务的特性(ACID)

  1. 原子性(Atomicity):一个事物的所有操作,要么全都完成,要么都不完成。如果在事务的执行过程中发生错误,则回滚到事务开始前的状态。
  2. 一致性(Consistency):在事务开始前和结束以后,数据库的完整性没有被破坏,即写入的资料必须完全符合所有预设约束、触发器、级联回滚等。
  3. 隔离性(Isolation):防止多个事务并发执行时由于交叉执行而导致数据的不一致。
  4. 持久性(Durability):事务结束后,对数据的修改是永久的。

Spring事务管理

  • 使用@Transactional注解对类或者方法进行事务管理
  • 当 @Transactional 注解的方法在类以外被调用的时候,Spring 事务管理才生效。(由于@Transactional 的工作机制是基于 AOP 实现的)

事务传播行为

  1. TransactionDefinition.PROPAGATION_REQUIRED(@Transactional注解默认事务传播行为):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  3. TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,就在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
  4. TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值