Spring、SpringMVC笔记

==>Spring框架

传统编码方式,代码相互间依赖性高,耦合性高,移植性差。比如删除一个dao实现类,service到controller层都会报错。

体系结构:
Spring、SpringMVC笔记

1、test部分

​ 2、Core container 核心容器

​ Bean:loC部分,如何创建对象,控制反转

​ Core:核心API

​ Context:上下文,读取配置文件等内容

​ spEL:表达式部分

​ 3、Aop 面向切面编程

​ 4、Aspects 切面

​ 5、国际化部分 提供了各种语言支持的版本

​ 6、message 消息部分

​ 7、DataAccess 数据访问

​ transaction:集成了事务管理机制

​ orm框架:集成了mybatis、hibernate

​ JDBC框架:spring data

​ 8、Web部分

​ SpringMVC框架、MVC框架

​ Servlet

一、 Beans 部分loC

一、JavaBean两种类型:

​ 1、实体类:封装数据的JavaBean

​ 满足如下要求:(1)提供公共的无参构造函数、(2)属性必须私有、(3)对私有属性封装成get及set方法。

​ 2、封装业务的JavaBean serviceimpl

​ 满足如下要求:(1)提供公共的无参构造函数、(2)提供公共的业务方法

二、自己创建Baen的缺点:耦合性强。

​ 所以我们把对象的创建、管理的权限交给Spring框架管理——控制反转loC

二、 创建Bean对象

【注】Spring 创建的 Bean 默认是单例模式

	 scope="prototype" 可以设定原型模式:每获取一次创建一个新的bean对象。

1 引入jar依赖

	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.16.RELEASE</version>
    </dependency>

2 创建spring-context.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="com.lg.dao.impl.UserDaoImpl"></bean>
</beans>

3 通过ApplicationContext来获取Spring创建的Bean

ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring-context.xml");
		//getBean中的名字要和xml中bean 的 id 一致
        UserDao userDao=(UserDaoImpl) applicationContext.getBean("userDao");

2.1 通过Spring解耦

public class UserServiceImpl implements UserService {

    private UserDao userDao=null;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public String addUser() {
        System.out.println("userservice:调用........");
        return userDao.addUser();
    }
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="ud" class="com.lg.dao.impl.UserDaoImpl"></bean>
    
    <bean id="us" class="com.lg.service.impl.UserServiceImpl">
        <!--        给UserDaoServiceImpl的属性通过set方法赋值-->
        <property name="userDao" ref="ud"></property>
    </bean>
</beans>

id:称为对象的标识,不能重复,因为我们创建的对象都在同一容器中。

2.2 依赖注入方式 DI

在Spring中给属性赋值称为依赖注入—— DI

方式一:set方法,设值注入;

 	<bean id="ud" class="com.lg.dao.impl.UserDaoImpl"></bean>

    <bean id="us" class="com.lg.service.impl.UserServiceImpl">
        <!--给UserDaoServiceImpl的属性userDao通过set方法赋值-->
        <property name="userDao" ref="ud"></property>
    </bean>

方式二:构造注入:通过构造函数进行注入。要求类必须有构造函数,以创建Student对象为例:

  <bean id="lg" class="com.lg.entity.Stu">
        <constructor-arg name="stuName" value="刘刚"></constructor-arg>
        <constructor-arg name="stuAge" value="18"></constructor-arg>
  </bean>

方式三:自动注入

<bean id="userDao" class="com.lg.dao.impl.UserDaoImpl"></bean>
<!--通过byName找到id名与实体类中属性名相同的bean,为实体类中的属性赋值-->
<bean id="userService" class="com.lg.service.impl.UserServiceImpl" autowire="byName"></bean>

2.3 Bean的创建时间

1、饿汉模式,在启用BeanFactory时就创建,默认是饿汉模式,提前创建好Bean,随用随取。

2、懒汉模式:启动BeanFactory时不创建bean,调用getBean() 方法时创建。

​ 设置方式:

	<beans default-lazy-init="true"> </beans>

2.4 Bean的作用域

1、默认单例模式

2、scope=“prototype” 原型模式;

五大作用域:singleton、Prototype、request、session、globalsession

2.5 Bean的不同类型属性的注入

<!--    创建UserBean-->
    <bean id="kb" class="com.lg.entity.User">
        <property name="id" value="200101"></property>
        <property name="pwd" value="123456"></property>
        <property name="sex" value="男"></property>
        <property name="age" value="20"></property>
        <!--        两种时间格式 推荐使用斜杠 -->
        <!--<property name="birthday" value="2001-2-20"></property>-->
        <property name="birthday" value="2001/2/20"></property>
        <!--        容器类型赋值-->
        <!--        数组-->
        <property name="hobby">
            <array>
                <value>编程</value>
                <value>打豆豆</value>
                <value>打篮球</value>
            </array>
        </property>
        <!--        set集合-->
        <property name="phones">
            <set>
                <value>15840666666</value>
                <value>15888888888</value>
                <value>15888887777</value>
            </set>
        </property>
        <!--        list集合-->
        <property name="names">
            <list>
                <value>刘刚</value>
                <value>全能刚</value>
            </list>
        </property>
        <!--        map集合-->
        <property name="countries">
            <map>
                <entry key="USA" value="美国"></entry>
                <entry key="KR" value="韩国"></entry>
                <entry key="RUSSIA" value="俄罗斯"></entry>
                <!--  <entry key="book" value-ref="id"></entry>-->
            </map>
        </property>
        <!--        Properties-->
        <property name="files">
            <props>
                <prop key="username">root</prop>
                <prop key="driverClass">com.mysql.JDBC.Driver</prop>
            </props>
        </property>
        <!--        对象集合 两种方式-->
        <!--        方式1-->
        <property name="bookList">
            <list>
                <bean id="javaSE" class="com.lg.entity.Book">
                    <property name="isnb" value="java11"></property>
                    <property name="bookName" value="java从入门到放弃"></property>
                </bean>
                <bean id="db" class="com.lg.entity.Book">
                    <property name="isnb" value="java22"></property>
                    <property name="bookName" value="闪酷跑路"></property>
                </bean>
            </list>
        </property>
        <!--        第二种方式 引入外部 bean 对象-->
        <property name="bookList2">
            <list>
                <ref bean="javaSE"></ref>
            </list>
        </property>
    </bean>

2.6 Bean的生命周期

Spring、SpringMVC笔记

【注】只有设值注入才走set方法,构造注入不走set方法。

​ 多例无销毁方法,等待垃圾回收机制GC去销毁。单例模式销毁时,先去找是否实现接口的destroy方法,再去找XML中配置的 destroy-method方法。

设置生命周期的两种方式:

方式一:XML配置方式,在实体类中自定义初始化与销毁的方法,手动在xml中调用。

<bean id="lg" class="com.lg.entity.Stu" init-method="" destroy-method="">
        <constructor-arg name="stuName" value="刘刚"></constructor-arg>
        <constructor-arg name="stuAge" value="18"></constructor-arg>
 </bean>

方式二:实体类中实现接口 implements InitializingBean, DisposableBean 两个接口

​ 重写接口中的初始化(new的时候调用)、销毁方法(销毁的时候调用)

​ 自动调用。

三、代理设计模式

作用:复杂的功能不让我们调用,只让我们调用简单的代理的方法。

3.1 静态代理模式

局限性:只能代理一种类型。

【案例】通过代理模式执行找房子方法

public interface HomeDao {
    public Integer getHome();
}
public class HomeDaoImpl implements HomeDao {
    public Integer getHome() {
        System.out.println("找到了房子");
        return 1;
    }
}

代理类:

public class HomeDaoProxy implements HomeDao {
   	private HomeDao homeDao;

    public HomeDao getHomeDao() {
        return homeDao;
    }

    public void setHomeDao(HomeDao homeDao) {
        this.homeDao = homeDao;
    }

    public Integer getHome() {
        return homeDao.getHome();
    }
}

测试:通过代理执行找房子方法。

public class TestProxy {
    public static void main(String[] args) {
        HomeDaoImpl homeDao=new HomeDaoImpl();

​        HomeDaoProxy proxy=new HomeDaoProxy();
​        proxy.setHomeDao(homeDao);

​        proxy.getHome();
​    }
}

3.2 动态代理模式

动态代理可以代理任何类型。

3.2.1 JDK动态代理

反射原理,需要实现接口。

public class DyProxy<T> implements InvocationHandler {
    //声明属性,接收所代理的对象
    private T target;
    public T getTarget() {
        return target;
    }
    public void setTarget(T target) {
        this.target = target;
    }
    public DyProxy() {
    }
    public DyProxy(T target) {
        this.target = target;
    }
    //执行invoke方法就是执行所要代理的方法
    //proxy——所代理的对象
    //method——代理所执行的方法
    //args——方法参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o=null;
        System.out.println("动态代理方法开始执行。。。");
        try {
            o=method.invoke(target, args);
        } catch (Exception e) {
            System.out.println("动态代理执行出现异常");
            e.printStackTrace();
        }finally {
            System.out.println("动态代理执行结束");
        }
        return null;
    }
}
public class TestProxy {
    public static void main(String[] args) {
        DeptDaoImpl deptDao = new DeptDaoImpl();
        InvocationHandler invocationHandler = new DyProxy<DeptDao>(deptDao);
        DeptDao dao = (DeptDao) Proxy.newProxyInstance(DeptDao.class.getClassLoader(), new Class[]{DeptDao.class}, invocationHandler);
        dao.login();
    }
}

3.2.2 cglib 动态代理

【原理】代理对象是被代理对象的子类

过滤器原理,无需实现任何接口,需要编写过滤器。

实现步骤:

​ 1、添加jar依赖

	<dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>

​ 2、编写被代理类及方法

​ 3、编写代理类(方法过滤器 implements MethodInterceptor)

public class CglibProxy implements MethodInterceptor {
    //enhancer对象用来生成代理对象的子类对象、这样就能回调被代理对象的方法
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz) {
        //继承代理对象,enhancer现在为被代理对象的子类
        enhancer.setSuperclass(clazz);
        //设置回调的方法,替代被代理对象回调这些方法
        enhancer.setCallback(this);
        //生成代理对象
        return enhancer.create();
    }

    //过滤器方法、拦截被代理对象里的方法
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置执行");
        //调用被代理对象的方法
        Object result = methodProxy.invokeSuper(o, objects);
        //执行之后
        System.out.println("后置执行");
        return result;
    }
}

​ 4、测试

public class TestCglib {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        //得到代理对象
        Student student = (Student) proxy.getProxy(Student.class);
        //执行方法会被代理类中的过滤器拦截
        student.sayHello();
    }
}

3.3.3 JDK动态代理与cglib动态代理区别

spring框架同时使用了JDK动态代理及cglib动态代理。

1、原理上的区别:

JDK动态代理:JDK利用反射机制生成一个代理接口的匿名类

cglib动态代理:继承被代理对象,回调被代理对象中的方法

2 、使用上的区别:

目标对象实现了接口,默认使用JDK动态代理,也可以指定cglib动态代理。没有实现接口的话只能使用cglib动态代理。

3、效率上区别:

jdk8以上,JDK动态代理效率高。

jdk6以后,对jdk动态代理进行了优化,当调用次数少时,jdk动态代理高;大量调用时,cglib效率高。

【特别强调】cglib不能对final类、final修饰的方法进行代理。

因为final类不能被继承,final修饰的方法不能被重写。

四、Aop

4.1 概念

oop:面向对象编程

Aop: 面向切面编程:将那些多个类都用到的的公共业务进行封装,封装后的结果称为切面。

【Aop原理】内部由动态代理技术执行,当执行核心功能时,代理去执行 核心功能相关的业务功能。因为执行核 心功能不但包括方法,还包括异常处理、事务处理等业务。

A—— aspect 切面

O—— 面向

P—— 编程

【本质】Aop是对oop的一种提升。

连接点:把程序中的核心方法(切入点)和通用业务进行封装,封装成 JoinPoint 对象,代理会把此对象传入到通 用业务中,在通用方法中就可以获取核心方法相关信息。

切入点:Pointcut,就是核心方法。

通知/增强:在不同的时间点所执行的“公共业务”,称为通知或增强

​ 1、在执行核心方法前所执行的公共业务,称为 前置增强。

​ 2、在执行核心方法后所执行的公共业务,称为 后置增强。

​ 3、在执行核心方法发生异常时所执行的公共业务,称为 异常处理增强。

​ 4、在执行核心方法的 finally{ } 所执行的业务功能,称为 最终增强。

​ 5、环绕增强,以上四种增强/通知的总称。

织入:将切入点与增强连接到一起,封装成连接点,这时会自动创建出动态代理对象,动态代理对象将会替我们执 行核心方法的配置好的一些业务(增强)。

4.2 springAop的使用步骤

【注】目标对象实现了接口,默认使用JDK动态代理,也可以指定cglib动态代理。没有实现接口的话只能使用cglib动态代理。

1、添加jar依赖

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.1.6.RELEASE</version>
</dependency>

2、编写公共业务类

public class DoException {
    //首先声明日志记录对象
    private final Logger logger = Logger.getLogger(DoException.class);

    //1、异常处理增强:当核心方法发生异常时,应该调用执行的公共业务
    public void catchException(JoinPoint joinPoint, RuntimeException e) {
        System.out.println("发生异常,异常信息:" + e.getMessage());
        //记录到日志中
        //获取核心方法的类\核心方法名
        logger.error("异常处理增强:" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法发生了异常,异常信息如下:"
                + e.getMessage());
    }

    //2、最终增强,执行finally{}时执行的方法
    public void runFinally(JoinPoint joinPoint) {
        logger.debug("最终增强:" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法执行结束");
    }
}
public class Oper {
    private final Logger logger = Logger.getLogger(Oper.class);

	//前置增强
    public void beforeOper(JoinPoint joinPoint) {
        logger.debug("前置增强:执行了" + joinPoint.getTarget() + "类里的" + joinPoint.getSignature() + "方法" +
                ",方法参数列表如下:" + Arrays.toString(joinPoint.getArgs()));
    }

    //后置增强
    public void afterOper(JoinPoint joinPoint, Object result) {
		//System.out.println(1/0);
        logger.debug("后置增强:" + joinPoint.getTarget() + "类中的" + joinPoint.getSignature() + "方法执行结束," +
                "方法的返回值" + result);
    }
}

3、在spring的配置文件中添加Aop相关的命名空间

4、配置文件中配置

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <bean id="dao" class="com.lg.dao.impl.UserDaoImpl"></bean>
    <bean id="service" class="com.lg.service.impl.UserServiceImpl">
        <property name="userDao" ref="dao"></property>
    </bean>
    <!--创建公共业务类的bean-->
    <bean id="exception" class="com.lg.aop.MyExce"></bean>
    <bean id="oper" class="com.lg.aop.MyOper"></bean>

    <aop:config>
        <!--创建切入点(也就是核心方法)-->
        <aop:pointcut id="pointcut" expression="execution(public void deleteUser())"/>

        <!--配置切面  第一个-->
        <aop:aspect ref="oper">
            <!--织入:将切入点与增强方法关联到一起,创建出连接点,这时会自动产生 动态代理 对象  -->
            <!--            配置前置增强 后置增强-->
            <aop:before method="beforeEnhance" pointcut-ref="pointcut"></aop:before>
            <aop:after-returning method="afterEnhance" pointcut-ref="pointcut"></aop:after-returning>
        </aop:aspect>

        <!--配置切面  第二个-->
        <aop:aspect ref="exception">
            <!--配置异常处理增强、最终增强-->
            <aop:after-throwing method="exceEnhance" pointcut-ref="pointcut" throwing="e"></aop:after-throwing>
            <aop:after method="finallyEnhance" pointcut-ref="pointcut"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

5、测试

【注意】loC获取的bean对象必须用接口去接收,否则默认JDK的动态代理方法将会报错。

public class TestAop {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-context.xml");
        UserService service = (UserService) applicationContext.getBean("us");
        //UserServiceImpl service2 = (UserServiceImpl) applicationContext.getBean("us");
        service.addUser();
    }
}

4.3 环绕增强

环绕增强实现了前四种增强,不能与前四种一起用。

1、编写公共业务类

ProceedingJoinPoint 继承 JoinPoint 相当于对连接点再次封装

public class Around {
    private final Logger logger = Logger.getLogger(Arround.class);

    //环绕增强处理
    public Object doArround(ProceedingJoinPoint joinPoint) {
        //存放切入点返回值
        Object result = null;
        //编写前置增强代码
        logger.debug("环绕增强-前置增强执行了:" + joinPoint.getTarget() + "中的" + joinPoint.getSignature() +
                "方法,参数是:" + Arrays.toString(joinPoint.getArgs()));
        //执行核心方法
        try {
            //执行核心方法,将核心方法从连接点中取出来执行
            result = joinPoint.proceed();
            logger.debug("环绕增强-后置增强:"+joinPoint.getTarget()+"方法执行结束,返回值为:"+result);
        } catch (Throwable throwable) {
            //环绕增强
            logger.error("环绕增强-异常处理增强:"+joinPoint.getTarget()+"方法中发生异常,异常信息:"+throwable.getMessage());
            throwable.printStackTrace();
        }finally {
            //最终增强
            logger.debug("环绕增强-最终增强"+joinPoint.getSignature()+"执行结束。");
        }
        return result;
    }
}

2、配置文件中配置切面

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <bean id="dao" class="com.lg.dao.impl.UserDaoImpl"></bean>
    <bean id="service" class="com.lg.service.impl.UserServiceImpl">
        <property name="userDao" ref="dao"></property>
    </bean>
    <!--    创建公共业务类的bean-->
    <bean id="around" class="com.lg.aop.Arround"></bean>

    <aop:config>
        <!--   ******************     创建切入点 ——> 环绕增强-->
        <aop:pointcut id="aroundPointcut" expression="execution(public String addUser())"/>
        
        <!--      ************  配置切面  环绕增强-->
        <aop:aspect ref="around">
            <aop:around method="doArround" pointcut-ref="aroundPointcut"></aop:around>
        </aop:aspect>
    </aop:config>

</beans>

4.4 实现接口方式织入增强

Spring、SpringMVC笔记

1、实现前置增强接口

public class BeforeAdvice implements MethodBeforeAdvice {
    //前置增强,无需在xml中配置织入增强,动态代理会自动识别执行
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        
    }
}

2、xml 头部信息

xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       "

4.5 通用配置切入点

通用方式语法:

execution( 访问修饰符 - 返回值类型 - 所在包和类(非必填)- 方法名 - 参数(非必填,只写参数类型))

例如:

​ (1)excution( * com.lg.aop.addUser() )—— 返回值类型不限*, com.lg.aop 包下的任何addUser()方法;

​ (2)excution(void com…*.addUser() )—— 访问修饰符任意的,返回值类型是void的,com包及其子包下的所有类,所有addUser() 方法;

​ (3)excution(* addUser(…))—— 访问修饰符任意,返回值任意,所有的addUser()方法,参数类型任意;

​ (4)excution(* addUser(…))—— 所有的addUser()方法,且方法参数任意。

【注】com.lg…* 表示 lg 包及其子包下的,所有类*

​ com.lg.* 表示 lg 包及下的所有类*

<aop:pointcut id="aroundPointcut2" 
						expression="execution(public * com.lg..*.addUser(..))"/>

五、spring与mybatis的整合

将一些原来配置到mybatis中的配置现在配置到spring-context.xml中

1、新建项目

2、添加jar依赖

<dependencies>
    	<dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.10</version>
        </dependency>
    </dependencies>

3、改变映射文件存放位置

pom.xml中

  <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

spring-context.xml中 也需要设置Mapper.xml的存放位置,见第五步。

4、编写连接数据库的配置文件 database.properties

【注意】整合时,xml中不能用${username}来获取数据库名,

​ 因为,在xml中${username}获取的是计算机的用户名。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/question_bank?useSSL=false&amp;characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456
maxWait=5000
initialSize=2
maxActive=6
minIdle=2

5、创建spring的配置文件 spring-context.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       ">
    <!--  1、  导入数据库配置文件  classpath:指定在根目录resources-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!--  2、  配置druid连接池    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--        通过设值注入给属性赋值-->
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <!--        配置初始化连接对象大小、最小活跃数、最大活跃数-->
        <property name="initialSize" value="${initialSize}"></property>
        <property name="minIdle" value="${minIdle}"></property>
        <property name="maxActive" value="${maxActive}"></property>
        <!--        配置最大等待时间-->
        <property name="maxWait" value="${maxWait}"></property>
        <!--        配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
        <!--        配置一个连接在池中最小生存时间-->
        <property name="minEvictableIdleTimeMillis" value="300000"></property>
    </bean>

    <!-- 3、  创建 SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        引入连接池-->
        <property name="dataSource" ref="dataSource"></property>
        <!--        告知接口对应的mapper映射文件所在位置-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/lg/dao/impl/*.xml</value>
            </list>
        </property>
        <!--配置实体类的 别名 -->
        <property name="typeAliasesPackage" value="com.lg.entity"></property>
    </bean>

    <!--4、配置MapperScannerConfigurer  在dao中通过这些配置就能得到sqlSession-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
        <!--<property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
        <property name="basePackage" value="com.lg.dao"></property>
        <!--  配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

    <bean id="us" class="com.lg.service.impl.UserServiceImpl">
        <!--这里的ref 写成dao的小写,因为程序已经为我们自动创建了id 为 userDao这个bean对象-->
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

【原理】(1)创建druid连接池;

​ (2)根据连接池创建出sqlSessionFactory对象

​ (3)配置MapperScannerConfigurer:将我们配置的sqlSessionFactory引入进来,然后自动调用sqlSessionFastory.openSession()方法创建出,sqlSesson对象;然后再配置告诉dao所在的包,检测出所有的dao接口,再自动调用sqlSession.getMapper方法,创建出所有的dao实现类的对象bean,这些bean的默认名字就是接口的小写,那个userDao就是自动创建出来的其中一个bean。

6、编写dao及Mapper.xml文件

​ 我们所有的dao接口的实现类对象bean都不用通过sqlSession.getMapper( *Mapper.xml) 获取了,通过配置,spring框架已经给我们创建好放在内存中了。这些bean的id名字默认都是dao接口的小写。

​ 如下面的userDao,就是自动创建好的一个放在内存中的bean对象。

	<bean id="us" class="com.lg.service.impl.UserServiceImpl">
        <!--这里的ref 写成dao的小写,因为程序已经为我们自动创建了id 为 userDao这个bean对象-->
        <property name="userDao" ref="userDao"></property>
    </bean>

7、编写service及实现类

六、事务

spring中默认集成了事务管理,每一个insert、delete、update都是一个独立的事务。

spring框架默认捕获的是runtimeException 如果spring框架默认捕获 CheckException 事务发生异常也会提交。解决方案:rollback-for=“Exception”

1、脏读:事务A读取了事务B改变了但是没有提交的数据,最终事务B回滚了;

2、不可重复读:事务A在多次读取的过程中,事务B对数据进行了更改提交,导致事务A多次读取的数据不一样

3、幻读:事务A将表中分数修改为具体的ABCDE级别,就在这时事务B又插入了一条数据,导致当事务A修改完成 后发现还有一条数据没有修改过来,就像发生幻觉一样。

6.1 事务的传播行为

Spring、SpringMVC笔记

6.2 事务的隔离级别

isolation,读取数据的问题。

【概念】一个事务与其他事务间的隔离程度,影响程度越小,隔离级别越高。

【作用】保证事务与事务间相互不影响,避免各种并发问题。

mySql默认隔离级别:可重复读。
Spring、SpringMVC笔记

6.3 XML配置事务步骤

这是 声明式事务!! 还有编程式事务 两种。

事务回滚时机:

(1)必须被拦截到—— 配置方式、注解方式;

(2)spring框架必须自动捕获到异常才会回滚 —— 如果发生异常被我们自己的catch捕获了,将不会被回滚;

​ 如果被catch了,还要回滚的话需要在catch中发生异常;

​ 最简单方式:不捕获异常;

​ 根据场景自己定义异常,让事务回滚。

1、创建一个事务管理器DatasourceTransactionManager,为其绑定数据源。

<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--        注入dataSource属性-->
        <property name="dataSource" ref="dataSource"></property>
</bean>

2、配置事务通知

 <!--    6、 配置事务通知  管理某些方法 事务隔离级别 及 传播行为-->
    <tx:advice id="txManager" transaction-manager="tx">
        <tx:attributes>
            <tx:method name="insertUsers" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRES_NEW" read-only="false"/>
        </tx:attributes>
    </tx:advice>

3、配置切面:事务实质上是一种增强/通知

<aop:config>
        <aop:pointcut id="txPointCut" expression="execution(public * insertUsers(..))"/>
        <aop:advisor advice-ref="txManager" pointcut-ref="txPointCut"></aop:advisor>
</aop:config>

完整代码:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx" 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
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--  1、  导入数据库配置文件  classpath:指定在根目录resources-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--  2、  配置druid连接池    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--        通过设值注入给属性赋值-->
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <!--        配置初始化连接对象大小、最小活跃数、最大活跃数-->
        <property name="initialSize" value="${initialSize}"></property>
        <property name="minIdle" value="${minIdle}"></property>
        <property name="maxActive" value="${maxActive}"></property>
        <!--        配置最大等待时间-->
        <property name="maxWait" value="${maxWait}"></property>
        <!--        配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
        <!--        配置一个连接在池中最小生存时间-->
        <property name="minEvictableIdleTimeMillis" value="300000"></property>
    </bean>

    <!-- 3、  创建 SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        引入连接池-->
        <property name="dataSource" ref="dataSource"></property>
        <!--        告知接口对应的mapper映射文件所在位置 相当于mapper注册-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/lg/dao/impl/*.xml</value>
            </list>
        </property>
        <!--        配置实体类的 别名 -->
        <property name="typeAliasesPackage" value="com.lg.entity"></property>
    </bean>

    <!--    4、配置MapperScannerConfigurer  通过配置能够获得所有dao接口的实现类对象bean-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--        告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
        <!--        <property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
        <property name="basePackage" value="com.lg.dao"></property>
        <!--        配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

  
    <!--   5、 创建事务管理器-->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--        注入dataSource属性-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    6、 配置事务通知  管理某些方法 事务隔离级别 及 传播行为-->
    <tx:advice id="txManager" transaction-manager="tx">
        <tx:attributes>
            <tx:method name="insertUsers" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRES_NEW"
                       read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <!--    7、事务也是一种增强 使用事务需要配置切面-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(public * insertUsers(..))"/>
        <aop:advisor advice-ref="txManager" pointcut-ref="txPointCut"></aop:advisor>
    </aop:config>

    <bean id="us" class="com.lg.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    
</beans>

七、注解配置

【注意】注解配置,需要在XML配置文件中告诉注解所要扫描的包;配置事务管理器;配置在注解中引入事务管理器。

类上方:

@service注解默认创建该service实体类的对象存放到内存中,默认对象名是实体类的小写字母
@Service(“usi”) 指定创建的对象名
@Component 通用
@Scope(“prototype”)多例模式

属性上方:

@Autowired注解应用在属性上方,按类型找属性对象并赋值
@Qualifier(“dd”) 一般与Autowired注解结合使用,首先按类型找,再按照属性名找
@value注入简单基本类型数据(8种基本+String)

事务:

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)————事务,可以加在类上、方法上

7.1 注解Ioc 、事务

@Service
//@Scope("prototype")
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class UserServiceImpl implements UserService {

    @Autowired
//    @Qualifier("dd")
    private UserDao userDao;

    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
    public Boolean insertUsers(List<User> userList) {
        Integer temp = 0;

        for (User user : userList) {
            temp += userDao.insertUser(user);
        }
        if (temp == userList.size()) {
            return true;
        }
        return false;
    }
}

7.2 注解Aop

1、注解配置切面类

@Aspect  //身份:切面
@Component  //创建切面类的bean,当前类属于未知
public class MyAdvice {

    @Pointcut("execution(public * com..*.*(..))")
    public void p1(){}

    @Before("p1()")
    public void doBefore(JoinPoint joinPoint){
        System.out.println("前置增强:"+joinPoint.getTarget()+"类的"+joinPoint.getSignature()+"方法执行。。。");
    }

    @AfterReturning(value = "p1()",returning = "returning")
    public void doAfter(JoinPoint joinPoint,Object returning){
        System.out.println("后置增强:"+joinPoint.getTarget()+"类的"+joinPoint.getSignature()+"方法执行,返回值:"+returning);
    }

    @AfterThrowing(value = "p1()",throwing = "ex")
    public void doException(JoinPoint joinPoint,Exception ex){
        System.out.println("发生异常,异常信息:"+ex.getMessage());
    }
    
    @After("p1()")
    public void doFinally(JoinPoint joinPoint){
        System.out.println("最终增强:"+joinPoint.getSignature()+"方法执行完毕");
    }
}

7.3 总配置文件

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx" 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
       http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--  1、  导入数据库配置文件  classpath:指定在根目录resources-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--  2、  配置druid连接池    -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--        通过设值注入给属性赋值-->
        <property name="driverClassName" value="${driver}"></property>
        <property name="url" value="${url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <!--        配置初始化连接对象大小、最小活跃数、最大活跃数-->
        <property name="initialSize" value="${initialSize}"></property>
        <property name="minIdle" value="${minIdle}"></property>
        <property name="maxActive" value="${maxActive}"></property>
        <!--        配置最大等待时间-->
        <property name="maxWait" value="${maxWait}"></property>
        <!--        配置间隔多久进行一次检测,检测关闭空闲的连接对象,单位毫秒-->
        <property name="timeBetweenEvictionRunsMillis" value="60000"></property>
        <!--        配置一个连接在池中最小生存时间-->
        <property name="minEvictableIdleTimeMillis" value="300000"></property>
    </bean>

    <!-- 3、  创建 SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--        引入连接池-->
        <property name="dataSource" ref="dataSource"></property>
        <!--        告知接口对应的mapper映射文件所在位置 相当于mapper注册-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/dao/impl/*.xml</value>
            </list>
        </property>
        <!--        配置实体类的 别名 -->
        <property name="typeAliasesPackage" value="com.entity"></property>
    </bean>

    <!--    4、配置MapperScannerConfigurer  通过配置能够获得所有dao接口的实现类对象bean存放在内存中,这些bean的默认名是接口的小写-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--        告诉dao接口所在的包,如果接口在多个包中,可以使用逗号分割-->
        <!--        <property name="basePackage" value="com.lg.dao,com.lg.dao2"></property>-->
        <property name="basePackage" value="com.dao"></property>
        <!--        配置得到sqlSession所需要的工厂 ,引入另外一个bean需要 ref ,但是此处通过value属性-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

    <!--   5、 创建事务管理器-->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--        注入dataSource属性-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--    6、类中事务引入事务管理器-->
    <tx:annotation-driven transaction-manager="tx"></tx:annotation-driven>
    <!--    7、告诉哪些包下有被注解的类、属性、方法。-->
    <context:component-scan base-package="com"></context:component-scan>
    <!--    8、启用注解 Aop,Aop默认使用XML配置方式-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

八、Spring-test & Junit

1、引入依赖

		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

2、测试使用

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class TestSpringJunit {
    @Autowired
    private UserDao userDao;

    @Test
    public void m1(){
        User user = userDao.selectUserByUserName("pg");
        System.out.println(user);
    }
}

==>SpringMVC 框架

M V C模式优点:

(1)低耦合,层次间不相互依赖,有利于程序变更;

(2)利于团队开发。

MVC框架都有哪些:Struts Struts2 springMVC

一、SpringMVC 开发流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOGSG2xx-1606655045684)(picture\7.png)]

1、引入jar依赖

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.8.RELEASE</version>
 </dependency>

2、配置前端控制器

在web.xml中配置。

名字:DispatctherServlet,由SpringMVC提供;

功能:使用DispatctherServlet接收,拦截的所有网络请求;

​ (1)解析:请求路径中的方法名,对应着控制器的方法

​ (2)适配:响应结果所对应的视图

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!--  配置核心控制器Servlet-->
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--      核心控制器需要读取的配置文件的位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!--  拦截所有请求 / 拦截所有请求,不包括jsp -->
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  
</web-app>

3、配置springMVC.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--        配置Servlet扫描的父包,子包的类、方法、属性都有注解-->
    <context:component-scan base-package="com.qf.controller"></context:component-scan>
    <!--    配置:mvc也开启使用注解实现-->
    <mvc:annotation-driven></mvc:annotation-driven>
    <!--    配置:视图解析器,-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--        到根目录下找-->
        <property name="prefix" value="/"></property>
        <!--        后缀为.jsp-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    在第一个拦截后,没找到,放行,再到tomcat默认的的servlet中,这个servlet帮我们找html资源-->
    <mvc:default-servlet-handler></mvc:default-servlet-handler>

</beans>

4、配置后端控制器

@Controller  //1、身份是控制器,2、使用注解方式创建出该类对象
public class UserController {
    //在方法上添加注解:请求路径所对应的方法
    @RequestMapping("/login")
    public String login() {
        System.out.println("调用了登录方法");
        return "login";
    }
    @RequestMapping("/register")
    public String register(){
        System.out.println("调用了注册方法");
        return "register";
    }
}

5、配置视图解析器

解析控制器返回的字符串所对应的视图位置

	<!--    配置:视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--        到根目录下找-->
        <property name="prefix" value="/"></property>
        <!--        后缀为.jsp-->
        <property name="suffix" value=".jsp"></property>
    </bean>

二、控制器获取表单数据

表单:

<form method="post" action="addUser2">
    <p>编号:<input type="text" name="id"/></p>
    <p>编号:<input type="text" name="username"/></p>
    <p>编号:<input type="text" name="password"/></p>
    <input type="submit" value="添加用户">
</form>

2.1 基本数据类型传参参数

【注意】形参字段名要与表单name属性一致!

复选框可以采用数组接收。

 @RequestMapping("/addUser")
    public String addUser(Integer id, String username, String password) {
        System.out.println("id:" + id + ",username:" + username + ",password:" +        		password);
        return "login";
    }

2.2 对象类型传参

底层采用set方法注入

【注意】对象属性名要与表单name属性一致,大小写也必须一致。

复选框可以采用对象数组接收。

@RequestMapping("/addUser2")
    public String addUser2(User user) {
        System.out.println(user);
        return "login";
    }

2.3 地址传参

JSP 页面:

<script>
    window.location.href="addUser3/liugang/18"
</script>

controller:

@RequestMapping("/addUser3/{username}/{age}")
    public String addUser2(@PathVariable("username")String name,@PathVariable("age") 		Integer age){
        
        System.out.println(name+"----------"+age);
        return "login";
    }

三、配置过滤器解决乱码问题

web.xml 文件中添加过滤器

	 <!--  3、配置过滤器解决乱码问题-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

四、控制器传值给View

@Controller
@RequestMapping("/user.do")
//设置存入request域的同时,存入session域
@SessionAttributes({"username", "password"})
public class UserModel {
    
    //1、存入model中的同时 数据存入 session 域
    //存入model(model存放在 request域)
    @RequestMapping("/getUser5")
    public String getUserList5(Model model) {
        model.addAttribute("username", "刘刚");
        model.addAttribute("password", "123456");
        return "forward:/user3.jsp";
    }

    //2、直接存到request域
    //3、直接存到session域
    @RequestMapping("/getUser")
    public String getUserList(HttpServletRequest request, HttpSession session) {
        List<User> list = new ArrayList<User>();
        list.add(new User(1, "pg", "123456"));
        list.add(new User(2, "kobe", "123456"));
        request.setAttribute("ul", list);
        session.setAttribute("uu", list);
        return "forward:/user.jsp";
    }

    //4、存到model对象中,相当于存放到request域
    @RequestMapping("/getUser2")
    public String getUserList2(Model model) {
        List<User> list = new ArrayList<User>();
        list.add(new User(1, "pg", "123456"));
        list.add(new User(2, "kobe", "123456"));
        model.addAttribute("uuu", list);
        return "forward:/user.jsp";
    }

    //5、存到 modelAndView 对象中,相当于存放到request域
    @RequestMapping("/getUser3")
    public ModelAndView getUserList3() {
        List<User> list = new ArrayList<User>();
        list.add(new User(1, "pg", "123456"));
        list.add(new User(2, "kobe", "123456"));
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("forward:/user2.jsp");
        modelAndView.addObject("list", list);
        return modelAndView;
    }

}

五、解决静态资源访问不到

在springMVC的配置文件中加入mvc:default-servlet-handler配置

​ 分析:web.xml 中 / 拦截所有请求到DispatcherServlet,找对应的映射,

​ 如果没有找到,不报异常,请求继续往下走,到DefaultServlet中,

​ DefaultServlet 会到项目下按照路径寻找静态资源

六、JSON处理

SpringMVC默认使用的JSON工具是jackson

6.1 常用 JSON 注解

1、@ResponseBody: 把响应给客户端的对象转成JSON格式

2、@RequestBody: 把客户端提交的JSON转成java对象

3、@RestController : @Controller+@ResponseBody

4、@JsonFormat(pattern=“yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)

​ 设置在前端解析时间的格式,东八区

	@JsonFormat(pattern = "yyyy-MM-dd mm:hh:ss",timezone = "GMT+8")
    private Date birthday;

5、@JsonProperty 给属性起别名

 	@JsonProperty("u_name")
    private String username;

6、@JsonIgnore:忽略某个属性,转json时,此属性不转

7、JsonInclude:当属性值为空或empty时,此属性不转JSON

 	@JsonInclude(JsonInclude.Include.NON_NULL)
    private String addr;

二、相关使用方法,如下:

1、导入依赖

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.8</version>
</dependency>

2、@ResponseBody 注解(方法上),表名该方法自动将数据转换成JSON格式,返给AJAX请求。

3、@RestController注解(类上方),具有 @Controller & @ResoponseBody 双重功效。表示类下所有方法都返 回JSON字符串

4、@RequestBody注解(方法形参列表中),作用:把前端AJAX提交的JSON数据,转成user对象.

function tj() {
        var xhr = new XMLHttpRequest();
        url = "/HelloSpringMVC_war_exploded/json/ajax";
        var user = '{"username": "刘刚", "password": "123456"}';
        xhr.open("post", url, true);
        xhr.setRequestHeader("content-type","application/json");
        xhr.send(user)
        xhr.onreadystatechange = function () {
            if (xhr.status == 200 && xhr.readyState == 4) {
                alert(xhr.responseText)
            }
        }
    }
//要求:JSON里name 和user对象的属性同名
@RequestMapping(value ="/ajax" , method=RequestMethod.POST,
                produces="text/html;charset=utf-8")
    @ResponseBody
    public String getAjax(@RequestBody User user) {
        System.out.println(user);
        return "响应数据";
    }

6.2 自定义序列化(转JSON)的规则

1、extends JsonSerializer 接口

public class MySeralize extends JsonSerializer<Double> {
    @Override
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        //value   就是转JSON的属性值
        //gen   按照自定义的规则,生成新值
        //BigDecimal.ROUND_HALF_UP  四舍五入
        String newValue= BigDecimal.valueOf(value).setScale(2,BigDecimal.ROUND_HALF_UP).toString();
        gen.writeNumber(newValue);
    }
}

2、在重写的public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException 方法中去定义自己的序列化规则

3、gen.writeNumber(newValue) 输出自定义后的新值

4、在具体的属性上应用

@JsonSerialize(using = MySeralize.class)
private double salary;

七、SpringMVC集成 fastjson

步骤:

1、引入fastjson依赖

<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.54</version>
</dependency>

2、注册fastjson替换掉原有的jsckson

<mvc:annotation-driven>
    <!-- 安装FastJson,转换器 -->
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <!-- 声明转换类型:json -->
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

3、使用fastjson

@RequestBody、@ResponseBody、@RestController 使用方式不变

@JSONField(name = "tea_No")  //别名
private int tea_Id;
@JSONField(serialize = false)  //不转换成JSON
private String tea_Name;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")  //格式化
private Date birthday;
@JSONField(serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
private String addr;
//使用自定义的序列化规则
@JSONField(serializeUsing = MySeralize2.class)
private Double salary;

自定义序列化规则:

public class MySeralize2 implements ObjectSerializer {
    @Override
    public void write(JSONSerializer jsonSerializer, Object o, Object o1, Type type, int i) throws IOException {
        //参数 o,是要转换的值
        //jsonSerializer  输出器:把转换后的值进行输出
        //转成Double
        double doubleChange=(Double)o;
        BigDecimal bdChange=BigDecimal.valueOf(doubleChange).setScale(2,4);
        String strChange="¥"+bdChange;
        jsonSerializer.write(strChange);
    }
}

八、异常解析器

概念: 控制器里的方法(Hander)被执行时,发生异常,并不能被代理对象自动调用增强来处理。如果每个 hander中的方法都 try-catch 捕获的话那么代码将会很冗余。

解决方案: 配置统一的异常解析器,当hander发生异常时,异常解析器进行统一的捕获、处理。

处理方法: 针对不同的异常,创建不同的异常界面。

实现步骤:

1、定义类实现HanderExceptionResover 接口

2、实现接口里的方法

​ 多路捕获异常

​ 声明异常界面:用于展示异常信息

//自定义的异常解析器
//当handler发生异常后,此异常解析器就能捕获异常,捕获后执行resolveException方法
public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse, Object o,Exception e) {
        //判断异常类型,跳转/转发到不同的错误页面即可
        ModelAndView modelAndView=new ModelAndView();
        if(e instanceof SQLException){
            modelAndView.addObject("meg","数据库发生异常");
            modelAndView.setViewName("forward:/error/dberror.jsp");
        }
        if(e instanceof NullPointerException){
            modelAndView.addObject("meg","发生未知异常");
            modelAndView.setViewName("forward:/error/exception.jsp");
        }
        return modelAndView;
    }
}

3、在springMVC配置文件中配置异常解析器即可

<bean class="com.qf.springmvc.utils.MyExceptionResolver"></bean>

8.2 实际开发中异常的处理方案

(1)在dao中抛出异常;

(2)在service中捕获:采用Aop切面异常处理增强;

(3)在控制器方法中(Hander),采用配置异常解析器,做统一处理

九、拦截器

如果有多个hander中有相同的逻辑代码,这是我们可以抽取出来交由拦截器统一处理。

1、实现HandlerInterceptor接口

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("调用hander之前");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("hander执行结束");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("页面渲染完成。。。");
    }
}

2、springMVC.xml中配置拦截器

 <!--    配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/user.do/**"/>
            <mvc:exclude-mapping path="/user.do/getUser"/>
            <bean class="com.qf.util.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

十、rest风格,称为restful

可读性更强的编码规范

1、每个资源都有唯一的标识URL

@Controller
@RequestMapping("/user")  //唯一标识   
public class UserController {
    @RequestMapping("/testSession")   //用户下的具体请求操作
    public String testSession(Model model){
        model.addAttribute("userName","刘刚");
        model.addAttribute("userId","liugang");
        model.addAttribute("pwd","123456");
        return "forward:/users.jsp";
    }

3、传参时: 资源?id=1&name=zhaoyan 不采用此种方式

​ users/1 采用路径传参

3、不同的请求采用不同的 http-method

  • get:查询操作
  • post:添加操作
  • delete:删除操作
  • put:修改操作

在后台控制器中,不再使用RequestMapping 使用具体的@PostMapping("/")

十一、跨域问题

报错:Access-Control-Allow-Origin

原因:跨域问题,浏览器不允许在提交网络请求时,有和当前服务器 协议、IP地址、端口号中任何一个不同的,将不 提交请求,称为同源策略。

跨域实现:

1、后台:通过注解设置允许的域

@CrossOrigin("http://127.0.0.1:8020")
public class RestUserController {

2、前端、

方式1:xhr.withCredentials=true;

方式2:jsonp方式

 function query() {
        var xhr=new XMLHttpRequest();
        var url="http://localhost:8085/springmvcpro_war/restUser/queryUser/1000"
        //xhr.withCredentials=true;
        xhr.jsonpCallback="myCallBack";
        xhr.open("get",url,true);
        xhr.send();
        xhr.onreadystatechange=function () {
            if(xhr.status==200 && xhr.readyState==4){
                var strData=xhr.responseText;
                var jsonData=JSON.parse(strData);
                document.write(jsonData.userId+"---"+jsonData.userName);
            }
        }
    }


    function query2(){
    	var url="http://localhost:8085/springmvcpro_war/restUser/queryUser/1000";
    	$.ajax({
    		type:"get",
    		url:url,
    		async:true,
    		jsonpCallback:"myCallBack",
    		success:function(strData){
    			alert(strData.userName);
    			 //var jsonData=JSON.parse(strData);
                //document.write(jsonData.userId+"---"+jsonData.userName);
    		}
    	});
    }


	//定义一个空函数
    function myCallBack(dt){
    }

十二、StringMVC执行流程

Spring、SpringMVC笔记

2:Handermapping在程序启动前执行,将requestMapping注解里的路径、以及请求方法和对应的Ioc容器中的 bean注册到map集合中;

3:根据处理请求:HanderAdapter进行适配, 获取请求头中的信息, 找到具体的hander以及拦截器的bean。如果有拦截器的话先进行拦截器,再进行参数绑定 执行方法 将参数、视图路径 放到ModelAndView 通过后置拦截返回DispatcherServlet。

上一篇:案例:人员管理系统


下一篇:基于aop切面实现日志拦截