【总结】spring基础
阅读原文时间:2023年07月09日阅读:3

一.spring

(1)核心容器(core container):由spring-core,spring-beans,spring-context和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成

(2)数据访问(data access):jdbc,orm(jpa,heribate),transactions

(3)web:web,web-mvc,web-socket

(4)其它:aop,AspectJ,测试模块(JUnit)

(1).classPathXmlApplicationContext:加载类路径下的配置文件(要求配置文件必须在类路径下,不在的话就加载不了)

(2).FileSystemXmlApplicationContext:加载磁盘任意路径下的配置文件(必须有访问权限)

(3).AnnotationConfigApplicationContext:读取注解创建容器

ApplicationContext继承BeanFactory接口,是一种更高级的容器,提供了更多功能(Aop,国际化,消息发送)

区别:BeanFactory在启动的时候不会实例化Bean,从容器拿bean的时候才会实例化。ApplicationContext在启动的时候就把所有Bean实例化

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--mybatis -->
    <context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="NEVER"/>
    <bean name="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${password}"/>
        <property name="driverClassName" value="${driver}"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSouce"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.kkb.dao"/>
    </bean>
      <!--mybatis END -->

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

</beans>

(1)bean元素:使用该元素描述需要spring容器管理对象

(2)name属性:给被管理的对象起个名字,获得对象时getBean("name值")

(3)class属性:被管理对象的完整类名

(4)id属性:与name属性一模一样,名称不可重复,不能使用特殊字符

id与name的区别?

①id是唯一的,配置文件中不允许出现两个id相同的。中允许出现两个name相同的,在用getBean()返回实例时,后面一个Bean被返回

②如果没有配置id和name,则用类的全名作为name

(5)scope:singleton,prototype,request,session

绝大多数情况下,使用单例singleton(默认值),但是在与struts整合时候,务必要用prototype多例,因为struts2在每次请求都会创建一个新的Action,若为单例,在多请求情况下,每个请求找找spring拿的都是同一个action

(6)初始化和销毁:init-method,destory-method。spring会在对象创建之后立刻调用 init-method 对应注解为@PreDestory,spring容器在关闭并销毁所有容器中的对象之前调用destory-method对应注解为@PostConstruct

(1)空参构造

<!-- 使用无参构造函数创建对象 -->
<bean id="bean3" class="cn.itcast.beans.Bean3"></bean>

(2)静态工厂创建

<!-- 静态工厂创建(调用静态方法创建) -->
<bean name="user" class="cn.itcats.UserFactory" factory-method="createUser"></bean>

public class Bean1_factory {
    public static Bean1 getBean(){
        return new Bean1();
    }
}
public class Bean1 {
    public void add(){
        System.out.println("bean1 ........");
    }
}

(3)实例工厂

需要配置两个bean,因为无法通过类名调用非静态方法

<!-- 使用实例工厂创建对象 -->
<bean id="bean2_fcty" class="cn.itcast.beans.Bean2_factory">
<bean id="bean2" factory-bean="bean2_fcty" factory-method="getBean"></bean>
public class Bean2_factory {
    public Bean2 getBean(){
        return new Bean2();
    }
}
public class Bean2 {
    public void add(){
        System.out.println("bean2 ........");
    }
}

(1)构造方法注入

使用对象的话,应改用ref属性

<bean id="user" class="bean.User">
    <constructor-arg index="0" name="username" value="zhangsan"></constructor-arg>
    <constructor-arg index="1" name="password" value="1234"></constructor-arg>
</bean>

(2)set注入

与构造器注入相比,只是少了index属性

<bean id="userService" class="com.lyu.spring.service.impl.UserService">
    <property name="userDao" ref="userDaoMyBatis"></property>
</bean>

(3)注解注入

①引用类型注入:

@Autowired:spring注解,默认是以byType的方式去匹配,如果没有找到,就通过byName的方式去查找

@Resource:java的注解,默认以byName的方式去匹配,如果没有找到就会以byType的方式查找

@Qualifier:(spring注解)指定某个具体名称的bean(配合autowired和resource使用)

②值类型注入

@Value("kaka")

@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;
public UserService(){


@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;


<!-- 注入复杂类型属性 -->

<bean id="user" class="com.siwuxie095.property.User">

    <!-- 数组 -->
    <property name="arr">
        <list>
            <value>小孙</value>
            <value>小李</value>
        </list>
    </property>

    <!-- List 集合 -->
    <property name="list">
        <list>
            <value>张三</value>
            <value>李四</value>
            <value>王五</value>
        </list>
    </property>

    <!-- Map 集合 -->
    <property name="map">
        <map>
            <entry key="a" value="老大"></entry>
            <entry key="b" value="老二"></entry>
        </map>
    </property>

    <!-- Properties 类型 -->
    <property name="properties">
        <props>
            <prop key="username">root</prop>
            <prop key="password">8888</prop>
        </props>
    </property>
</bean>


<!--spring随项目的创建而创建,随项目的关闭而关闭,避免创建多个ApplicationContext-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--指定加载spring配置文件的位置-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>



@RunWith(SpringJUnit4ClassRunner.class) //RunWith用于指定junit运行环境,是junit提供给其他框架测试环境接口扩展
@ContextConfiguration("classpath:applicationContext.xml") //配置xml文件
public class Demo {

    @Autowired
    private User user;

    @Test
    public void fun01() {
        System.out.println(user);
    }
}

(1)JoinPoint(连接点):目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。

(2)Pointcut(切入点):目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。

(3)Advice(通知/增强) :增强方法的代码、想要的功能

(4)Target(目标对象):被代理对象,被通知的对象,被增强的类对象

(5)Weaving(织入):将通知应用到连接点形成切入点的过程

(6)Proxy(代理):将通知织入到目标对象之后形成的代理对象

(7)aspect(切面):切入点+通知

@Before 前置通知(Before advice) :在某连接点(JoinPoint)——核心代码(类或者方法)之前执行的通知,但这个通知不能阻止连接点前的执行。为啥不能阻止线程进入核心代码呢?因为@Before注解的方法入参不能传ProceedingJoinPoint,而只能传入JoinPoint。要知道从aop走到核心代码就是通过调用ProceedingJionPoint的proceed()方法。而JoinPoint没有这个方法。

这里牵扯区别这两个类:Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下 JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。这样你就能明白 proceed方法的重要性。

@After 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

@AfterReturning 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。

@Around 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。这时aop的最重要的,最常用的注解。用这个注解的方法入参传的是ProceedingJionPoint pjp,可以决定当前线程能否进入核心方法中——通过调用pjp.proceed();

@AfterThrowing 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。

@Aspect //@Aspect注解代表该类是个通知类,书写切点表达式@Pointcut("execution(返回值 全类名.方法名(参数))")
public class LogAspect {

    //抽取公共的切入点表达式
    //1、本类引用
    //2、其他的切面引用
    @Pointcut("execution(* com.gz.MathCalculator.*(..))")
    public void pointCut(){};

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
    }

    @After("com.gz.LogAspect.pointCut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
    }

    @AfterReturning(value="pointCut()",returning="result")
    public void logReturn(JoinPoint joinPoint,Object result){
        System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
    }

    @AfterThrowing(value="pointCut()",throwing="exception")
    public void logException(JoinPoint joinPoint,Exception exception){
        System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
    }

}

(1)@Before 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知

(2)@After 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)

(3)@AfterReturning 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况

(4)@AfterThrowing 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知

(5)@Around 环绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行

spring中事务可以分为编程式事务控制和声明式事务控制

编程式事务:自己手动控制事务,就叫做编程式事务控制。 【细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制】比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚

声明式事务:Spring提供了对事务的管理, 这个就叫声明式事务管理【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务】 (因为aop拦截的是方法)

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

(1)propagation_required:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

(2)propagation_supports:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}

// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}
单纯的调用methodB时,methodB方法是非事务的执行的。当调用methdA时,methodB则加入了methodA的事务中,事务地执行

(3)propagation_mandatory:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常

(4)propagation_requires_new:它会开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起(需要使用 JtaTransactionManager作为事务管理器)

(5)propagation_not_supported:总是非事务地执行,并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED(也需要使用JtaTransactionManager作为事务管理器)

(6)propagation_never:总是非事务地执行,如果存在一个活动事务,则抛出异常。

(7)propagation_nested: 如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按propagation_required属性执行(嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚)

它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。

(1)使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。

(2)使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager支持

(1)开启注解

<tx:annotation-driven/>

(2)方法上增加@Transactional注解方法就可以被事务管理起来

@Transactional注解的属性:

readOnly : 是否仅仅只读。默认读写都可以

timeout : 事务超时时间,默认没有超时时间

isolation: 事务的隔离级别 默认:TransactionDefinition.ISOLATION_DEFAULT(大部分数据库的默认隔离级别是可重复读)

propagation :事务的传播属性 默认:TransactionDefinition.PROPAGATION_REQUIRED