Spring框架初步

spring配置文件

<!--一个bean标签对应一个对象-->
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl"/>
<!--spring可以创建一个非自定义类的对象,存在的某个类-->
<bean id="myDate" class="java.util.Date"/>
public class MyTest {
    @Test
    public void test(){
        //指定spring配置文件名称和位置
        String config = "applicationContext.xml";
        //创建容器(同时创建配置文件中所有的对象,默认调用无参数构造方法)
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        
        //获取所有对象个数(常用方法)
        int num = ac.getBeanDefinitionCount();
        System.out.println(num);
        //获取所有对象的名称,返回一个数组(常用方法)
        String names []= ac.getBeanDefinitionNames();
        for (String name : names){
            System.out.println(name);
        }
        
        //从spring容器中使用id获取对象
        SomeService service = (SomeService)ac.getBean("someService");
        service.doSome();
        
        Date date = (Date)ac.getBean("myDate");
        System.out.println(date);
    }

di依赖注入

注入:赋值
spring中Java的基本数据类型和String都是简单类型
di:给属性赋值
1.set注入(设值注入):
(1)简单注入:spring调用set方法完成属性赋值(类中必须有set方法)(在构造方法之后执行)
执行的是set方法,没有属性有set方法也可执行,如email(Student中没有email属性)
xml规则:所有value都得在""中,与数据类型无关
(2)引用类型的set注入:ref

	<bean id="myStudent" class="com.bjpowernode.ba01.Student" >
        <property name="name" value="李四"/>
        <property name="age" value="20"/>
        <property name="email" value="lisi@qq.com"/>
        <property name="school" ref="mySchool"/>
    </bean>
            
    </bean>
    <!--声明School对象-->
    <bean id="mySchool" class="com.bjpowernode.ba02.School" >
        <property name="name" value="广东工业大学"/>
        <property name="address" value="广州"/>
    </bean>

2.构造注入:spring调用类的有参数构造方法,在创建对象的同时赋值
(可以使用index索引的方式index=“0“ value=“张飞”)
构造注入创建文件对象(文件类有构造方法

3.引用类型自动注入:spring框架根据某些规则给引用类型赋值。
(1)byName(按名称注入):java类中引用类型的属性名和spring容器中配置文件的id一样,且数据类型一样,这样容器中的bean,spring能够赋值给引用类型。
( 2)byType(按类型注入):java类中引用类型的数据类型和spring容器中配置文件的class类型是同源关系,这样容器中的bean,spring能够赋值给引用类型。(类型一样,父子类关系,接口和实现类关系)只能符合其中一个同源关系,因为需要bean唯一

<bean id="myStudent" class="com.bjpowernode.ba05.Student" autowire="byType" >
    <property name="name" value="张飞"/>
    <property name="age" value="25"/>
    <!--<constructor-arg name="school" ref="mySchool"/>-->
</bean>

<bean id="mySchool" class="com.bjpowernode.ba05.School" >
    <property name="name" value="广东工业大学"/>
    <property name="address" value="广州"/>
</bean>

4.多配置文件的使用
一个总的文件包含其他的多个配置文件,一般不定义对象,可以用通配符*

<import resource="classpath:其他配置文件的路径"/>

基于注解的di

需要在类中加入注解,例如@Component
在spring配置文件中添加组件扫描器和加载属性配置文件(有属性配置文件的话)
组件是Java对象,指定注解在项目中的包名
spring会扫描遍历指定的包,把包中所有的注解按照功能创建对象,或给属性赋值
包的指定方式三种:使用多次扫描器,在一个扫描器中用";“或者”,"隔开包,指定父包(不建议)

<context:component-scan base-package="com.bjpowernode.ba01"/>
<context:property-placeholder location="classpath:01.properties"/>

简单类型的属性赋值:@Value("") value是String类型

  • 1.在属性定义上面,无需set方法(推荐)
  • 2.在set方法上面

引用类型的属性赋值:@Autowired(有个require属性,默认是true(推荐),可以设置为false:找不到引用类型就返回null不报错)

  • 1.在属性定义上面,无需set方法(推荐)
  • 2.在set方法上面

@Autowired使用的是自动注入原理,支持byName,byType(默认)
byName自动注入比byType在@Autowired下面还需要加多@Qualifier(value=“bean的id”)**

@Component("myStudent")
public class Student {
    //使用配置文件,中文会乱码
    @Value("${myname}")
    private String name;
    @Value("22")
    private int age;

    @Autowired
    //@Qualifier("mySchool")
    private School school;
    }

jdk注解:
引用类型的属性赋值:@Resource:来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以用于引用类型注解,使用自动注入原理,支持byName(默认),byType

  • 1.在属性定义上面,无需set方法(推荐)
  • 2.在set方法上面
  • 先使用byName自动注入,如果赋值失败,则用byType
  • 只使用byName:@Resource(name = “bean的id”)
@Component("myStudent")
public class Student {

    @Value("张飞")
    private String name;
    @Value("22")
    private int age;

    @Resource
    //@Resource(name = "mySchool")
    private School school;
}

aop面向切面编程

public class MyIncatonHandler implements InvocationHandler {
    //目标对象
    private Object target;//SomeServiceImpl类

    public MyIncatonHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行MyIncatonHandler中的invoke()");
        //通过动态代理增强功能
        Object res = null;
        String methodName = method.getName();
        if("doSome".equals(methodName)){
            ServiceTools.doLog();
            res = method.invoke(target,args);//SomeServiceImpl.doSome
            ServiceTools.doEnd();
        }else{
            res = method.invoke(target,args);//SomeServiceImpl.
        }

        return res;
    }
}
public class MyApp {
    public static void main(String[] args) {
        SomeService target = new SomeServiceImpl();
        InvocationHandler handler = new MyIncatonHandler(target);
        //使用Proxy创建代理对象
        SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),handler);
        //通过代理执行方法
        proxy.doSome();//doSome传给MyIncatonHandler中invoke()的method
        System.out.println("============================");
        proxy.doOther();//doOther传给MyIncatonHandler中invoke()的method
    }
}

aop-aspectJ

需要在spring配置文件声明d对象,声明自动代理生成器

<!-- 声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
     创建代理对象是在内存中实现的,修改目标对象的内存中的结构。
     所以目标对象就是被修改后的代理对象
     aspectj-autoproxy 会把spring容器中所有的目标对象一次性生成代理对象
-->
<aop:aspectj-autoproxy />
<!-- 添加这个后,IDEA自动在上面帮助添加了约束文件 -->

前置通知

//注解表示当前类是切面类,给业务方法增加功能的类,其中放功能代码
@Aspect
public class MyAspect {
    //value完整写法,AspectJ 的切入点表达式(pdf第26页)
    //value写错则找不到代理对象,也没有功能增强
    @Before(value = "execution(public void com.bjpowernode.ba01.SomeServiceImpl.doSome(String,Integer))")
    //value简略写法,AspectJ 的切入点表达式(pdf第26页)
    //@Before(value = "execution(* *..SomeServiceImpl.do*(..))")
    //JoinPoint可以获取方法的相关信息,必须放在第一个参数位置
    public void myBefore(JoinPoint jp){
        //获取方法的完整定义和名称
        System.out.println("方法的定义="+jp.getSignature());
        System.out.println("方法的名称="+jp.getSignature().getName());
        //获取方法的实参
        Object args[] = jp.getArgs();
        for(Object arg:args){
            System.out.println("实参:"+arg);
        }
        //切面功能代码
        System.out.println("1---前置通知方法,在目标方法执行之前执行");
    }

后置通知
public,方法有参数,推荐是Object,有返回值
在目标方法之后执行,能获取目标方法的返回值

//returning 自定义变量 表示目标方法的返回值 自定义变量名必须和通知方法的形参名一样(下面的两个res)
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    //JoinPoint放在第一个参数,否则报错
    public void myAfterReturning(JoinPoint jp,Object res){

        System.out.println("方法的定义="+jp.getSignature());

        System.out.println("后置通知,在目标方法之后执行,获取的返回值="+res);
        //在后置通知中修改res的值是没有用的
    }

环绕通知
public,方法有参数,固定的参数ProceedingJoinPoint,必须有一个返回值,推荐Object
环绕通知经常做事务,在目标方法前开启事务,在目标方法后提交事务(事务管理)
功能最强的通知,在目标方法前后都能增强:

  • 控制目标方法是否被调用执行
  • 修改目标方法的执行结果

等同于invoke(InvocationHandler接口),ProceedingJoinPoint等同于Method

	//ProceedingJoinPoint继承JoinPoint,可以直接使用
    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
        String name = "";
        //获取参数值
        Object args[] = pjp.getArgs();
        if(args!=null && args.length > 1){
            Object arg = args[0];
            name = (String)arg;
        }
        //实现环绕通知
        Object res = null;
        System.out.println("环绕通知,在目标方法之前增强功能");
        //目标方法调用
        //符合条件则调用目标方法,控制目标方法是否执行
        if("zhangsan".equals(name)) {
            res = pjp.proceed();//等同于method.invoke();
        }
        System.out.println("环绕通知,在目标方法之后增强功能");
        //在目标方法的前或者后加入功能

        //目标方法的执行结果可以修改
        if(res!=null){
            res="hello";
        }
        //最后返回值
        return res;
    }

异常通知(了解)
public,方法参数有一个Exception,如果还有就是JoinPoint,没有返回值
在目标方法抛出异常时执行(做异常的监控程序,用try catch)

	//throwing 自定义变量 表示目标方法抛出的异常对象,变量名必须和方法名的参数一样
	@AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",throwing = "ex")
    public void myAfterThrowing(Exception ex){
        System.out.println("异常通知:方法发生异常时执行:"+ex.getMessage());
    }

最终通知(了解)
public,方法没有参数,如果有就是JoinPoint,没有返回值
总是会执行,在目标方法之后,一般用于资源清除工作

多个value相同复用时可以用@Pointcut,一个辅助功能,用方法名称代替切入点表达式

@Aspect
public class MyAspect {

    @After(value = "mypt()")
    public void myAfter(){
        System.out.println("最终通知:总是会执行的代码");
    }

    @Before(value = "mypt()")
    public void myBefore(){
        System.out.println("前置通知");
    }

    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
    public void mypt() {
    }
}

动态代理

代理对象:由框架创建的一种内存中的对象,通过这个对象执行方法时,可以执行切面的功能代码,实现功能的增强
proxy.getClass().getName()
目标类有接口,默认使用jdk动态代理
目标类没有接口,使用的是cglib动态代理,spring框架自动应用cglib
目标类有接口也可以用cglib动态代理,在spring.xml配置文件中修改:
在aop中加入 proxy-target-class=“true”

spring集成mybatis

使用spring的ioc(控制反转)技术,把mybatis中使用的对象交给spring框架创建,管理,赋值
1.使用连接池
2.创建SqlSessionFactroy对象
3.创建dao代理对象,访问数据库

mybatis.xml配置文件

<!-- settings:控制mybatis全局行为 -->
    <settings>
        <!-- 配置日志功能 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!--设置别名-->
    <typeAliases>
        <!--name:实体类所在包名-->
        <package name="com.bjpowernode.domain"/>
    </typeAliases>

    <mappers>
        <!--告诉 mybatis 要执行的 sql 语句的位置-->
        <!--name是包名,包中所有的mapper.xml一次都能加载-->
        <package name="com.bjpowernode.dao"/>
    </mappers>

spring.xml配置文件
声明数据源,用于连接数据库(使用属性配置文件properties)

<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据库连接池-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
		init-method="init" destroy-method="clone" >
	<!--set注入给DruidDataSource提供连接数据库信息-->
	<!--使用属性配置文件中的信息,语法:${key}-->
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
	<property name="maxActive" value="20" />
</bean>

创建SqlSessionFactory

<!--声明mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--set注入,把数据库连接池赋给dataSource属性-->
    <property name="dataSource" ref="myDataSource" />
    <!--mybatis主配置文件的位置,
        configLocation属性是Resource类型,读取配置文件赋值使用value,
        指定文件路径,使用classpath:表示文件位置
    -->
    <property name="configLocation" value="classpath:mybatis.xml" />
</bean>

生成每个dao接口的代理对象

<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
    MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--指定SqlSessionFactory对象的id-->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    <!--指定包名,dao接口所在的包
        MapperScannerConfigurer会扫描这个包中的所有接口,
        每个接口都执行一次getMapper()方法,得到每个接口的dao对象,
        创建好的对象放入spring的容器中,dao对象的默认名称是接口名的首字母小写
    -->
    <property name="basePackage" value="com.bjpowernode.dao" />
</bean>

让service可以用dao

<!--声明service,将上面创建的dao赋给属性,让service可以用dao-->
<bean id="studentService" class="com.bjpowernode.service.impl.StudentServiceImpl">
    <property name="studentDao" ref="studentDao"/>
</bean>

使用dao对象

public void testDaoInsert(){
    String config = "applicationContext.xml";
    ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
    //获取spring容器中的dao对象(dao对象的默认名称是接口名的首字母小写studentDao)
    StudentDao dao = (StudentDao)ctx.getBean("studentDao");
    Student student = new Student();
    student.setId(1007);
    student.setName("李白");
    student.setAge(28);
    student.setEmail("libai@qq.com");
    int num = dao.insertStudent(student);
    System.out.println("num="+num);
}

使用service对象(通过service访问dao)

public void testServiceAdd(){
    String config = "applicationContext.xml";
    ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
    StudentService service = (StudentService)ctx.getBean("studentService");
    Student student = new Student();
    student.setId(1008);
    student.setName("韩信");
    student.setAge(27);
    student.setEmail("hanxin@qq.com");
    int num = service.addStudent(student);
    //spring和mybatis整合在一起时,事务自动提交的。无需执行SqlSession.commit();
    System.out.println("num="+num);
}

spring的事务处理

需要在sping配置文件声明事务管理器,开启事务管注解驱动

<!--使用spring的事务处理(service的实现类中)-->
<!--1.声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--连接数据库,指定数据源-->
    <property name="dataSource" ref="myDataSource" />
</bean>
<!--2.开启事务管注解驱动,transaction-manager:事务管理器对象的id-->
<tx:annotation-driven transaction-manager="transactionManager"/>

1.使用service的实现类完成事务处理

使用spring的事务处理,需要放在public上面
@Transactional(
propagation = Propagation.REQUIRED,//传播行为
isolation = Isolation.DEFAULT,//隔离级别
readOnly = false,
rollbackFor = { //发生指定的异常就一定回滚
NullPointerException.class, NotEnoughException.class//自定义异常
}
@Transactional直接这样写则所有属性使用默认值

@Transactiona
@Override
public void buy(Integer goodsId, Integer nums) {
    //销售记录
    System.out.println("=====buy开始====");
    Sale sale = new Sale();
    sale.setGid(goodsId);
    sale.setNums(nums);
    saleDao.insertSale(sale);//insert into sale(gid,nums) values(#{gid},#{nums})

    //判断
    Goods goods = goodsDao.selectGoods(goodsId);
    if (goods == null){
        throw new NotEnoughException("编号:"+goodsId+",商品不存在");
    }else if (goods.getAmount() < nums){
        throw new NotEnoughException("编号:"+goodsId+",商品库存不足");
    }

    //修改库存
    Goods buyGoods = new Goods();
    buyGoods.setId(goodsId);
    buyGoods.setAmount(nums);
    goodsDao.updateGoods(buyGoods);//update goods set amount = amount - #{amount} where id = #{id}
    System.out.println("=====buy结束====");
}   

2.使用 AspectJ 的 AOP 配置管理事务(不使用@Transactiona)

配置spring.xml配置文件

        <!--声明式事务处理,和源代码完全分开-->
        <!--1.声明事务管理器对象-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!--连接数据库,指定数据源-->
            <property name="dataSource" ref="myDataSource" />
        </bean>
        <!--2.声明业务方法的事务属性(隔离级别,传播行为,超时时间)
            id:自定义名称
            transaction-manager:事务管理器对象的id
        -->
        <tx:advice id="myAdvice" transaction-manager="transactionManager">
            <!--tx:attributes:配置事务属性-->
            <tx:attributes>
                <!--tx:method给具体方法配置事务属性,method可以有多个,给不同的方法设置事务属性
                    name:方法名称,不带包和类,可以使用*
                    propagation:传播行为,枚举值
                    isolation:隔离级别
                    rollback-for:指定异常发生回滚
                -->
                <!--spring框架中tx:method name先找的是完整名称,再是带有通配符的名称如add*,最后是*-->
                <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
                           rollback-for="com.bjpowernode.excep.NotEnoughException,java.lang.NullPointerException"/>
            </tx:attributes>
        </tx:advice>

        <aop:config>
            <!--配置切入点表达式
                id:切入点表达式名称
                expression:切入点表达式,指定哪些类要用事务,aspectJ会创建代理对象

                例如下面写的是所有service类
            -->
            <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

            <!--配置增强器:关联advice和pointcut
                advice-ref:tx:advice配置的id
                pointcut-ref:切入点表达式的id
            -->
            <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
        </aop:config>

spring与web

controller类中

//web中创建spring容器对象
//String config = "spring.xml";
//ApplicationContext ctx = new ClassPathXmlApplicationContext(config);

//spring中直接获取ServletContext中创建好的容器对象来用
//WEB容器在启动时,会为每个WEB应用程序都创建一个ServletContext对象,代表当前web应用
WebApplicationContext ctx = null;

/*String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
Object attr =  getServletContext().getAttribute(key);
if (attr != null){
    ctx = (WebApplicationContext)attr;
}*/
//以上代码可以使用框架中的方法替代,获取对象
ServletContext sc = getServletContext();
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);

监听器

web.xml配置文件

<!--注册监听器ContextLoaderListener
    监听器被创建对象后会读取/WEF-INF/spring.xml(因为需要创建ApplicationContext对象,需要加载配置文件)
    /WEF-INF/applicationContext.xml是监听器默认读取的配置文件路径
    可以使用context-param重新指定位置

    配置监听器目的:创建容器对象,把spring.xml配置文件中所有的对象创建好
-->
<context-param>
    <!--contextConfigLocation表示配置文件的路径-->
    <param-name>contextConfigLocation</param-name>
    <!--自定义配置文件的路径:target/classes/spring.xml-->
    <param-value>classpath:spring.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

==================================================================================

spring事务的传播机制及原因分析:

  • PROPAGATION_REQUIRED : 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
  • PROPAGATION_SUPPORTS : 支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY : 支持当前事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW : 新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED : 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER : 以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值