Spring 框架基础

Spring 框架基础

IOC的基本应用

概念

1.什么是IOC

IOC:Inversion of Control (控制反转)。

描述的事情:Java开发领域对象的创建和管理问题。

传统的开发方式:在类A依赖类B的时候,往往会在类A中new一个B对象。

public class A{
    B b = new B();
    b.hello();
}

IOC思想下的开发方式:不用自己去new对象,而是由IOC容器去实例化对象并进行管理。我们需要用到哪个对象,去IOC容器中拿取。

此时,创建和管理对象的权力(控制),交给了外部环境(反转)。

// IOC容器
Map map = new HashMap<>();
// 往IOC容器中放入已经实例化好的对象
map.put("beanName", instance);
// 需要使用IOC容器中的对象时
map.get("beanName");
2.IOC解决了什么问题

对象之间的耦合问题。

传统的开发方式:

// 定义操作数据库的接口类
public interface AccountDao {}

// 利用传统JDBC实现操作数据库的接口
public class JdbcAccountDaoImpl implements AccountDao {}

// 业务逻辑层
public class TransferServiceImpl implements TransferService {
    // 此时操作数据库的方式为 传统的JDBC
    private AccountDao accountDao = new JdbcAccountDaoImpl();
}

// 若此时抛弃传统JDBC实现操作数据库 改用Mybatis实现操作数据库
public class MybatisAccountDaoImpl implements AccountDao {}

// 需要将业务逻辑层代码改为
public class TransferServiceImpl implements TransferService {
    // 此时操作数据库的方式为 Mybatis
    private AccountDao accountDao = new MybatisAccountDaoImpl();
}

// 若业务层代码中有“成千上万”个地方需要更换new的对象,此时的工作量会巨大。不符合Java面向接口编程的习惯。

IOC思想下的开发方式:

// 业务逻辑层
public class TransferServiceImpl implements TransferService {
    // 此时只定义需要用到的接口 不去new实现
    private AccountDao accountDao;
}
3.IOC和DI的区别

DI:Dependancy Injection(依赖注入)。

IOC和DI描述都是对象实例化及依赖关系维护这件事,只是站在的角度不同。IOC是站在对象的角度,将对象实例化和管理的权限交给了容器。DI是站在容器的角度,容器会把对象依赖的其他对象注入。

基本使用

纯XML模式

pom.xml

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.1.12.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-webartifactId>
    <version>5.1.12.RELEASEversion>
dependency>

resources\applicationContext.xml

<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
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="accountDao" class="com.demo.dao.impl.JdbcAccountDaoImpl" 
          scope="singleton" init-method="init" destroy-method="destroy">
        
        
        
        

        
        <constructor-arg index="0" ref="connectionUtils">constructor-arg>
        <constructor-arg index="1" value="zhangsan">constructor-arg>
        <constructor-arg index="2" value="1">constructor-arg>
        <constructor-arg index="3" value="100.3">constructor-arg>

        
        
        
        
    bean>
    
    
    <bean id="transferService" class="com.demo.service.impl.TransferServiceImpl">
        <property name="AccountDao" ref="accountDao">property>
        <property name="myArray"><array><value>1value><value>2value>array>property>
        <property name="myMap"><map><entry key="1" value="1"/><entry key="2" value="2"/>map>property>
        <property name="mySet"><set><value>1value><value>2value>set>property>
        <property name="myProperties">
            <props><prop key="1">value1prop><prop key="2">value2prop>props>
        property>
    bean>
    
    
    
    <bean id="connectionUtils" class="com.demo.utils.ConnectionUtils"/>
    
    <bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" 
          factory-method="getInstanceStatic">
    
    <bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"/>
    <bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" 
          factory-bean="createBeanFactory" factory-method="getInstance"/>
beans>
public class CreateBeanFactory {
    // 在静态方法中实例化对象 将自己 new 的对象要放到容器中
    public static ConnectionUtils getInstanceStatic() {
        return new ConnectionUtils();
    }

    // 实例化方法
    public ConnectionUtils getInstance() {
        return new ConnectionUtils();
    }
}
启动

JavaEE方式

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

JavaSE方式


<context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>

XML + 注解模式

resources\applicationContext.xml


    
    
    <context:component-scan base-package="com.demo"/>
    
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    bean>
beans>
xml形式 对应的注解形式
bean标签 @Component:注解加载类上。bean的id默认为类名的首字母小写。
@Controller:用于控制层
@Service:用于服务层
@Repository:用于Dao层
scope属性 @Scope(“prototype”),默认单例,注解加在类上
init-method属性 @PostConstruct,注解加在⽅法上,该⽅法就是初始化后调⽤的⽅法
destroy-method属性 @PreDestory,注解加在⽅法上,该⽅法就是销毁前调⽤的⽅法
DI依赖注入的方式 @Autowired
org.springframework.beans.factory.annotation.Autowired
默认按照类型注入。如果一个类型的实现方式有很多需要配合@Qualififier去告诉Spring配置哪个对象。

纯注解模式

@Configuration
@ComponentScan({"com.demo"})
public class SpringConfig {
}
启动

JavaEE方式

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

JavaSE方式


<context-param>
    <param-name>contextClassparam-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContextparam-value>
context-param>

<context-param>
    <param-name>contextConfigLocationparam-name>
    <param-value>com.lagou.edu.SpringConfigparam-value>
context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
注解 描述
@PropertySource 引⼊外部属性配置⽂件
@Import 引⼊其他配置类
@Value 对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
@Bean 将⽅法返回对象加⼊ SpringIOC 容器
@Component
@PropertySource({"classpath:jdbc.properties"})
public class DataSourceConfig {
    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    
    // 自己 new 一个对象 放入 IOC 容器
    @Bean
    public DruidDataSource createDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driverClassName);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
    
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

高级特性

lazy-init 延迟加载

概念:ApplicationContext 容器的默认行为是在启动服务的时候将所有 singleton 类型的 bean 提前进行实例化。将 lazy-init 设置为 true,bean 将不会再 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 索取 bean 时实例化。此属性对 scope=“pototype” 的 bean 不生效。


<bean id="lazyBean" calss="cn.demo.LazyBean" lazy-init="false" />
<beans default-lazy-init="true">beans>

特殊情况:存在两个bean。bean2 设置 lazy-init = true;bean1 设置 lazy-init = false,此时 bean2 会延迟加载。但是若 bean2 被 bean1 引用,在实例化 bean1 的同时,bean2也会被实例化

FactoryBean

概念:Spring 有两种bean:一种普通的 bean,一种工厂 bean。我们可以借助 FactoryBean 接口生成某一类型的 bean 实例。

// 利用 ResultBean 创建 Result 类型的 bean
public class ResultBean implements FactoryBean<Result> {
   @Override
    public Result getObject() throws Exception {
        Result result = new Result();
        return result;
    }
    
    @Override
    public Class<?> getObjectType() {
        return Result.class;
    }
}

// 获取 Result 类型的 bean
applicationContext.getBean("resultBean");

// 获取 ResultBean 工厂 bean
applicationContext.getBean("&resultBean");

SpringBean 的生命周期

1)创建 beanFactory。可以实现 BeanFactoryPostProcessor 接口拿到 beanFactory。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanFactoryPostProcessor:" + beanFactory);
    }
}

2)初始化BeanPostProcessor。

@Component
public class MyBeanPostprocessor implements BeanPostProcessor {
    public MyBeanPostprocessor() {
        System.out.println("2)初始化MyBeanPostprocessor");
    }
}

3)bean的构造方法。

public class LifeCycle implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    public LifeCycle() {
        System.out.println("3)LifeCycle的构造方法");
    }
}

4)BeanNameAware 接口。

@Override
public void setBeanName(String name) {
	System.out.println("4)BeanNameAware:" + name);
}

5)BeanFactoryAware 接口。

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    System.out.println("5)BeanFactoryAware:" + beanFactory);
}

6)ApplicationContextAware 接口。

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    System.out.println("6)ApplicationContextAware:" + applicationContext);
}

7)BeanPostProcessor 接口的 postProcessBeforeInitialization 方法。

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("7)postProcessBeforeInitialization:" + beanName);
    return bean;
}

8)@PostConstruct 注解标注的方法。

@PostConstruct
public void PostConstruct() {
    System.out.println("8)PostConstruct");
}

9)InitializingBean 接口的 afterPropertiesSet 方法。

@Override
public void afterPropertiesSet() throws Exception {
    System.out.println("9)InitializingBean:afterPropertiesSet");
}

10)xml 中 init-method 方法。

public void initMethod() {
    System.out.println("10)initMethod");
}

11)BeanPostProcessor 接口的 postProcessAfterInitialization 方法。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("11)postProcessAfterInitialization:" + beanName);
    return bean;
}

12)ApplicationContext 的创建。

public void TestLifeCycle() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    System.out.println("12)TestLifeCycle:" + context);
    context.close();
}

13)@PreDestroy 注解标注的方法。

@PreDestroy
public void PreDestroy() {
    System.out.println("13)PreDestroy");
}

14)DisposableBean 接口的 destroy 方法。

@Override
public void destroy() throws Exception {
    System.out.println("14)DisposableBean:destroy");
}

15)xml 中 destroy-method。

public void destroyMethod() {
    System.out.println("15)destroyMethod");
}

AOP的基本应用

概念

1.什么是AOP

AOP:Aspect Oriented Programming(面向切面编程)。

OOP:Object Oriented Programming(面向对象编程)。

AOP 是 OOP 的延续。OOP 三大特征:封装,继承,多态。是一种垂直继承体系。

OOP开发方式:

Public class Animal {
    public void eat(){
        System.out.println("eating...");
	}
    
    public void run(){
        System.out.println("running...");
	}
} 

Public class Cat extends Animal {
    // 此时不需要额外的再书写 eat 和 run 方法 解决了大多数代码重复问题
    // 然而如果每个 System.out.println() 前后需要增加大量重复的代码 此时 OOP就不能解决代码重复问题
    // 而且将业务代码和 日志或性能监控代码混杂在一起,代码臃肿,维护不方便
}

AOP优化方式:

public class ProxyFactory {
    // JDK 动态代理
    public Object getJdkProxy(Object obj) {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return getObject(method, args, obj);
            }
        });
    }

    private Object getObject(Method method, Object[] args, Object obj) throws SQLException, IllegalAccessException, InvocationTargetException {
        // 此处可以编写横切逻辑代码...
        Object result = null;
        try {
            result = method.invoke(obj, args);
            // 此处可以编写横切逻辑代码...
        } catch (Exception e) {
            // 此处可以编写横切逻辑代码...
            throw e;
        }
        return result;
    }

    // cglib 动态代理
    public Object getCglibProxy(Object obj) {
        return Enhancer.create(obj.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return getObject(method, objects, obj);
            }
        });
    }
}
2.AOP中的术语
名词 解释
连接点 Joinpoint 可以插入横切逻辑代码的地方
切入点 Pointcut 已经插入横切逻辑代码的地方
通知/增强 Advice 横切逻辑代码
目标对象 Target 被代理的对象
代理 Proxy 代理对象
织入 Weaving 插入横切逻辑代码的过程
切面 Aspect 横切逻辑代码整合后的类
3.Spring中AOP的代理选选择

默认情况下,Spring 会根据被代理对象是否实现了接口来选择是否使用 JDK动态代理 还是 cglib动态代理。当没有实现接口的时候选择 cglib 动态代理,当实现接口的时候会选择 JDK官方的代理技术,我们也可以通过配置的方式让 Spring 强制使用 cglib。

基本使用

纯XML模式

pom.xml

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-aopartifactId>
    <version>5.1.12.RELEASEversion>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>

resources\applicationContext.xml

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

http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd


<aop:config>
    <aop:aspect id="logAspect" ref="logUtils">
        
        <aop:pointcut id="pt1" expression="execution(public int com.demo.service.impl.TransferServiceImpl.transfer(java.lang.String,java.lang.String, int))"/>
        
        
        <aop:before method="beforeMethod" pointcut-ref="pt1"/>
        
        <aop:after method="afterMethod" pointcut-ref="pt1"/>
        
        <aop:after-returning method="successMethod" returning="rtValue" pointcut-ref="pt1"/>
        
        <aop:after-throwing method="exceptionMethod" pointcut-ref="pt1"/>
        
        <aop:around method="aroundMethod" pointcut-ref="pt1"/>
    aop:aspect>
aop:config>

XML + 注解模式


<aop:aspectj-autoproxy expose-proxy="true"/>
@Component
@Aspect
public class LogUtils {

    @Pointcut("execution(* com.demo.service.impl.TransferServiceImpl.*(..))")
    public void pt1() {
    }

    @Before("pt1()")
    public void beforeMethod(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        // 可以拿到 所有传递进来的参数
        for (Object arg : args) {
            System.out.println(arg);
        }
        System.out.println("业务逻辑开始执行之前执行.......");
    }

    // finally
    @After("pt1()")
    public void afterMethod() {
        System.out.println("业务逻辑结束时执行,无论异常与否都执行.......");
    }

    @AfterThrowing("pt1()")
    public void exceptionMethod() {
        System.out.println("异常时执行.......");
    }

    @AfterReturning(value = "pt1()",returning = "rtValue")
    public void successMethod(Object rtValue) {
        System.out.println(rtValue);
        System.out.println("业务逻辑正常返回时执行.......");
    }

//    @Around("pt1()")
    public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint) {
        System.out.println("在执行方法之前");
        try {
            // 控制原有业务逻辑是否执行
            proceedingJoinPoint.proceed();
            System.out.println("在执行方法之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("有异常时执行");
        } finally {
            System.out.println("最终执行");
        }
    }
}

纯注解模式

@EnableAspectJAutoProxy
public class SpringConfig {
}

Spring 声明式事务的支持

事务的四大特性(ACID)

特性 描述
原子性 atomicity 事务是一个不可分割的工作单位,事务中的操作要么同时发生,要么同时失败。
一致性 consistency 事务必须使数据库从一个一致性状态变换为另一个一致性状态。不会存在一个中间状态。
隔离性 isolation 当多个用户并发产生事务的时候,每个事务不能被其他事务干扰,事务相互之间隔离。
持久性 durability 事务一旦提交,改变就是永久性的。

事务的隔离级别

不采用隔离级别会发生的事情
错误情况 描述
脏读 事务A 读取到了 事务B **未提交 **的数据。
幻读 事务A 读取到了 事务B insert 或 delete 提交后 的数据。导致前后读取的条数不一致。
不可重复读 事务A 读取到了 事务B update提交后 的数据。导致前后读取的内容不一致。
数据库定义的四种隔离级别
隔离级别 描述
串行化 Serializable 最高。避免上述三种情况。
可重复度 Repeatable Read 第二。MySQL的默认隔离级别。可避免脏读不可重复读。update时会有行锁。
读已提交 Read Committed 第三。可避免脏读
读未提交 Read Uncommitted 第四。无法避免上述三种情况。

常用的事务传播行为

事务往往定义在 service 层。如果 service 层方法 A 调用了另一个 service 层方法 B,A 和 B 本身都被添加了事务,那么 A 调用 B 的时候,就需要进行一些协商,这就叫做事务的传播行为。A 调用 B,我们需要站在 B 的角度来定义事务的传播行为。

1)PROPAGATION_REQUIRED:B 如果没有事务,就新建一个事务。如果 A 已经存在一个事务,就加入到A的事务中。这是常见的选则。用于增删改查。

2)PROPAGATION_SUPPORTS:B 支持 A 的事务,如果 B 没有事务,就以非事务方式执行。常用于查询。

基本使用

纯XML模式

pom.xml

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-aopartifactId>
    <version>5.1.12.RELEASEversion>
dependency>
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-jdbcartifactId>
    <version>5.1.12.RELEASEversion>
dependency>
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-txartifactId>
    <version>5.1.12.RELEASEversion>
dependency>

resources/applicationContext.xml

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

http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd


<aop:config>
    <aop:advisor advice-ref="txAdvice" 
                 pointcut="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
aop:config>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        
        
        <tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT"/>
        
        <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
    tx:attributes>
tx:advice>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg name="dataSource" ref="dataSource"/>
bean>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>
XML + 注解模式

resources/applicationContext.xml


<tx:annotation-driven transaction-manager="transactionManager"/>
@Service("transferService")
public class TransferServiceImpl implements TransferService {
@Override
    // 配置声明式事务 注解里面有默认值
    @Transactional
    public void transfer() throws Exception {
    }
}
纯注解模式
@Configuration
@ComponentScan({"com.demo"})
@EnableTransactionManagement
public class SpringConfig {
}

// 自己 new 一个对象 放入 IOC 容器
@Bean
public DruidDataSource createDataSource() {
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName(driverClassName);
    druidDataSource.setUrl(url);
    druidDataSource.setUsername(username);
    druidDataSource.setPassword(password);
    return druidDataSource;
}

// JdbcTemplate 放入容器
@Bean
public JdbcTemplate createJdbcTemplate(DruidDataSource druidDataSource) {
    return new JdbcTemplate(druidDataSource);
}

// DataSourceTransactionManager 声明式事务放入容器
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DruidDataSource druidDataSource) {
    return new DataSourceTransactionManager(druidDataSource);
}

参考git:https://gitee.com/zhangyizhou/learning-spring-demo.git

1.自定义 ioc 和 aop 项目:lagou-transfer

1.spring ioc 纯xml项目:lagou-transfer-iocxml

2.spring ioc xml +注解项目;aop 纯xml项目:lagou-transfer-ioc-xml-anno

3.spring ioc+aop 纯注解项目:lagou-transfer-ioc-anno

4.spring 声明周期项目:lagou-spring-life-cycle

5.声明式事务项目:lagou-transfer-transaction

你可能感兴趣的:(spring,java,后端)