SSM笔记

1、Spring

1.1、Spring的开发步骤

  1. 导入依赖
	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.9.RELEASE</version>
    </dependency>
  1. 创建Bean
public interface UserDao {

    void save();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("save running...");
    }
}
  1. 创建applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--scope="prototype" -->
    <bean id="userDao" class="com.qin.dao.impl.UserDaoImpl" scope="prototype"></bean>

</beans>
  1. 创建ApplicationContext对象Bean
public class Test {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

1.2、Spring配置文件

  1. Bean标签范围配置:
取值范围说明
singleton默认值,单例的
prototype多例的
requestWEB 项目中,Spring创建一个 Bean 的对象,将对象存入到request 域中
sessionWEB 项目中,Spring创建一个 Bean 的对象,将对象存入到session 域中
global sessionWEB 项目中,应用在Portlet 环境,如果没有 Portlet 环境那么globalSession相当于 session
  • 当scope的取值为**singleton**时:

    • Bean的实例化个数:1个
    • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
    • Bean的生命周期:
      • 对象创建:当应用加载,创建容器时,对象就被创建了
      • 对象运行:只要容器在,对象一直活着
      • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
  • 当scope的取值为**prototype**时

    • Bean的实例化个数:多个
    • Bean的实例化时机:当调用getBean0方法时实例化Bean
      • 对象创建:当使用对象时,创建新的对象实例
      • 对象运行:只要对象在使用中,就一直活着
      • 对象销毁:当对象长时间不时,被Java的垃圾回收器回收了

  1. Bean生命周期的配置
  • init-method:指定类中的初始化方法名称
  • destroy-method: 指定类中销毁方法名称

在bean的实现类中添加:

    public void init(){
        System.out.println("初始化方法...");
    }

    public void destory(){
        System.out.println("销毁方法...");
    }

  1. Bean实例化的三种方式
  • 无参构造方法实例化(重要)

  • 工厂静态方法实例化

    • 创建工厂实例
    public class StaticFactory {
    
        public static UserDao getUserDao(){
            return new UserDaoImpl();
        }
    }
    
    • 配置文件中配置
        <bean id="userDao" class="com.qin.factory.StaticFactory" factory-method="getUserDao"></bean>
    
  • 工厂实例方法实例化

    • 创建工厂实例

      public class StaticFactory {
      
          public UserDao getUserDao(){
              return new UserDaoImpl();
          }
      }
      
    • 配置文件中配置

    //先注册实例工厂的bean
    <bean id="factory" class="com.qin.factory.DynamicFactory"/>
    //然后使用factory-bean="factory" factory-method="getUserDao" 方法
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao" />
    

1.3、Spring配置文件–依赖注入

  • 依赖注入方式:

set方法

set方法调用dao层,就是注入dao层,然后给Dao层写set方法;

public class UserServiceImpl implements UserService {

    private UserDao userDao;

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

    @Override
    public void save() {
        userDao.save();
    }
}
		<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl"></bean>
        <bean id="userService" class="com.qin.service.impl.UserServiceImpl">
            <property name="userDao" ref="userDao"/>
        </bean>

set方法注入:(另外一种方法)
P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下首先,需要引入P命名空间:

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

修改注入方式,就不需要注入子标签了

<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.qin.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

构造方法

  • 有参构造

set方法调用dao层,就是注入dao层,然后给Dao层写有参方法;(但一般也会写上无参方法);

public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public UserServiceImpl() {
    }

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}
<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl"></bean>	
<bean id="userService" class="com.qin.service.impl.UserServiceImpl">
   <constructor-arg name="userDao" ref="userDao"/>
</bean>

注入数据的三种注入类型

  • 引用数据类型;

  • 普通数据类型;

    • 在Dao层的实现类中;

      	private String username;
      	private int age;
      
          public void setUsername(String username) {
              this.username = username;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      	@Override
          public void save() {
              System.out.println(username+"===="+age);
          }
      

      然后在配置文件中;

      		<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl">
                  <property name="username" value="秦鹏"/>
                  <property name="age" value="18"/>
              </bean>
      
  • 集合数据类型;

    • 在Dao层的实现类中;
    	private List<String> strList;
        private Map<String, User> userMap;//要写一个User类
        private Properties properties;
    
        public void setStrList(List<String> strList) {
            this.strList = strList;
        }
    
        public void setUserMap(Map<String, User> userMap) {
            this.userMap = userMap;
        }
    
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    	@Override
        public void save() {
            System.out.println(strList);
            System.out.println(userMap);
            System.out.println(properties);
        }
    
    • 然后在配置文件中;
    	<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl">
            <!--list-->
            <property name="strList">
                <list>
                    <value>zs</value>
                    <value>ls</value>
                </list>
            </property>
            <!--map-->
            <property name="userMap">
                <map>
                    <entry key="user1" value-ref="user1"></entry>
                    <entry key="user2" value-ref="user2"></entry>
                </map>
            </property>
            <!--properties-->
            <property name="properties">
                <props>
                    <prop key="p1">ppp1</prop>
                    <prop key="p2">ppp2</prop>
                </props>
            </property>
        </bean>
    
        <bean id="user1" class="com.qin.domain.User">
            <property name="name" value="汤姆"/>
            <property name="addr" value="苏州"/>
        </bean>
        <bean id="user2" class="com.qin.domain.User">
            <property name="name" value="李飒"/>
            <property name="addr" value="北京"/>
        </bean>
    

    引入其他配置文件:(分模块开发)

    实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载;

    <import resource="applicationContext-xxx.xml"/>
    

    Spring重点配置

    <bean>标签
    	id属性:在容器中Bean实例的唯一标识,不允许重复;
    	class属性:要实例化的Bean的全限定名;
    	scope属性:Bean的作用范围,常用是singleton(默认)和prototype
        <property>标签:属性注入
    		name属性:属性名称
            value属性:注入的普通属性值
    		ref属性:注入的对象引用值
    		<list>标签
    		<map>标签
            <properties>标签
    		<constructor-arg>标签
    <import>标签:导入其他的spring的分文件
    

1.4、Spring相关API

  • ApplicationContext继承体系

applicationContext:接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象;

在这里插入图片描述

applicationContext的实现类:

  • ClassPathXmlApplicationContext ( 它是从类的根路径下加载配置文件推荐使用这种)

  • FileSystemXmlApplicationContext(它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置)

  • AnnotationConfigApplicationContext(当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。)


1.5、Spring配置数据源

1、数据源(连接池)的作用

  • 数据源(连接池)是提高程序性能如出现的;
  • 事先实例化数据源,初始化部分连接资源;
  • 使用连接资源时从数据源中获取;
  • 使用完毕后将连接资源归还给数据源;

常见的数据源(连接池):DBCPC3P0BoneCPDruid等;

2、抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件的方式:

  • 首先,需要引入context命名空间和约束路径;
  • 命名空间:xmlns:context="http://www.springframework.org/schema/context
  • 约束路径:
    • http://www.springframework.org/schema/context
    • http://www.springframework.org/schema/context/spring-context.xsd
    <!--加载外部的properties文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="JdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

3、使用方式

public class DataSourceTest {

    /**
     * 测试Spring容器创建产生数据源对象
     * @throws Exception
     */
    @Test
    public void test4() throws Exception {
        ClassPathXmlApplicationContext app = new 	ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = app.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();

        System.out.println(connection);
        connection.close();
    }

    /**
     * 测试手动创建C3P0数据源(加载properties文件方式)
     * @throws Exception
     */
    @Test
    public void test3() throws Exception {
        //读取配置文件
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");

        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String username = rb.getString("jdbc.username");
        String password = rb.getString("jdbc.password");

        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        Connection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }

    /**
     * 测试手动创建Druid数据源
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("jdbc:mysql://localhost:3306/test");
        dataSource.setPassword("123456");

        DruidPooledConnection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }

    /**
     * 测试手动创建C3P0数据源
     * @throws Exception
     */
    @Test
    public void test1() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        Connection connection = dataSource.getConnection();

        System.out.println(connection);

        connection.close();
    }
}

1.6、Spring注解开发

Spring是轻代码而重配置的架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xm配置文件可以简化配置,提高开发效率。

Spring原始注解

  • Spring原始注解主要是替代====的配置
注解说明
@Component使用在类上用于实例化Bean
@Controller使用在类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

前四个注解是一组的,@Controller、@Service、@Repository是@Component的衍生注解;

原始注解应用:

  • Bean的实现类中改变(Dao层)
//@Component代替<bean id="userDao" class="com.qin.dao.impl.UserDaoImpl"></bean>
//@Component("userDao")
@Repository("userDao")  //使用在dao层类上用于实例化Bean
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("save running...");
    }
}
  • Service的实现类中改变(Service层)
//@Component代替<bean id="userService" class="com.qin.service.impl.UserServiceImpl">
//@Component("userService")
@Service("userService") //使用在service层类上用于实例化Bean
public class UserServiceImpl implements UserService {

    //<property name="userDao" ref="userDao"/>
    //@Autowired  @Qualifier这两个标签代替了子标签注入
    //只使用@Autowired  按照数据类型从Spring容器中进行匹配的
    @Autowired
    //@Qualifier  按照名称(ID)从Spring容器中进行匹配的;!!!注意:@Qualifier注解要和@Autowired  结合使用!!!
    @Qualifier("userDao")

    //@Resource(name = "xxxx") @Resource相当于@Autowired加@Qualifier
    @Resource(name = "userDao")
    private UserDao userDao;

    //使用xml配置文件方式的话,就需要set方法;使用注解的话,就不需要写set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

不要忘记在配置文件中开启组件扫描

		<!--组件扫描-->
        <context:component-scan base-package="com.qin"></context:component-scan>

注入普通属性:

	//@Value 注解可以注入普通的值,也可以根据key从Spring容器中找到对应的值。使用SPL表达式
    @Value("${jdbc.driver}")
    private String driver;

Spring的作用范围:

在service方法上面添加注解:注意:每次只能使用一种,两个同时用会报错

@Scope("singleton")
@Scope("prototype")

Bean的初始化和销毁方法,注解的使用;

  • 在service层方法里面,写两个方法;方法上添加@PostConstruct @PreDestroy注解
	@PostConstruct
    public void init(){
        System.out.println("Service对象的初始化方法...");
    }

    @PreDestroy
    public void destory(){
        System.out.println("Service对象的销毁方法...");
    }

Spring新注解

使用原始注解,不能全部替代xml配置文件;还需要用注解替代的配置如下:

  • 非自定义的Bean的配置: ;
  • 加载properties文件的配置: <context;property-placeholder>;
  • 组件扫描的配置: context:component-scan;
  • 引入其他文件: ;

新注解:

注解说明
@Configuration用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包。
作用和在 Spring 的xml配置文件中的<context:component-scan base-package=“com.itheima”/>一样
@Bean使用在方法上,标注将该方法的返回值存储到Spring 容器中
@PropertySource用于加载.properties文件中的配置
@lmport用于导入其他配置类

Spring全注解的使用

  • 创建一个类,做为SPring的核心配置类;
//标志此类是Spring的核心配置类
@Configuration
//@ComponentScan("com.qin")代替<context:component-scan base-package="com.qin"></context:component-scan>
@ComponentScan("com.qin")
//加载外部的properties
//@PropertySource("classpath:jdbc.properties")替代<context:property-placeholder location="jdbc.properties"/>
//@PropertySource("classpath:jdbc.properties")

//导入分支的配置类
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {

}
//加载外部的properties
//@PropertySource("classpath:jdbc.properties")替代<context:property-placeholder location="jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;


    //Spring会将当前方法的返回值以指定名称存储到Spring容器中
    @Bean("dataSource")
    public DataSource getDataSource() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }

}
  • 要修改web类中的方法:改为:AnnotationConfigApplicationContext(xxxxx)
public class UserController {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext app = new 				AnnotationConfigApplicationContext(SpringConfiguration.class);
        UserService userService = app.getBean(UserService.class);
        userService.save();
        app.close();
    }

}

Spring整合junit

  • 导入依赖
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>

        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
  • 使用;创建一个test类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
//使用xml配置文件的时候使用
//@ContextConfiguration("classpath:applicationContext.xml.md")
public class SpringJunitTest {

    @Autowired
    private UserService userService;

    @Test
    public void test(){
        userService.save();
    }

}

1.7、Spring集合Web环境

ApplicationContext应用上下文

获取方式

  • 应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次。
  • 在Web项目中,可以使用==ServletContextListener==监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

ServletContextListener的使用步骤

  • 导入依赖
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
  • 在web.xml中配置
	<!--配置监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
	<!--全局初始化参数-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
  • 使用
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }

2、SpringMVC

2.1、概述

  • SpringMVC是一种基于Java 的实现MVC设计模型的请求驱动类型的轻量级Web 框架,属于SpringFrameWork的后续产品,i已经融合在Spring Web Flow中。

  • SpringMVC已经成为目前最主流的MV框架之一,并且随着Spring3.0的发布,全面超越Struts2,成为最优秀的MVC 框架。它通过一套注解,让一个简单的Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful编程风格的请求。

2.2、开发步骤

  • 导包
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
  • web.xml中配置Servlet(核心控制器DispathcerServlet)
	<!--springmvc前端控制器-->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  • 编写controller
@Controller
public class UserController {

    @RequestMapping("/quick")
    public String save(){
        System.out.println("save ruuing...");
        return "success.jsp";
    }

}
  • 配置Spring-mvc.xml文件(配置组件扫描)
	<!--组件扫描-->
    <context:component-scan base-package="com.qin.controller"/>

2.3、SpringMVC的执行流程

  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器;
  3. 处理器映射器找到具体的处理器可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
  4. DispatcherServlet调用HandlerAdapter处理器适配器;
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器);
  6. Controller执行完成返回ModelAndView;
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet;
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器;
  9. ViewReslover解析后返回具体View;
  10. DispatcherServlet根据View进行染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

2.4、SpringMVC注解解析

  • @RequestMapping(“xxxx”)
    • 作用:用于建立请求URL和处理请求方法之间的对应关系;
    • 位置
      • 类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录;
      • 方法上,请求URL的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径;
    • 常用参数
      • value: 用于指定请求的URL,它和path属性的作用是一样的;

      • method: 用于指定请求的方式;

      • params: 用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样;

        • params =[“accountName”],表示请求参数必须有accountName
        • params=[“moeny!100"},表示请求参数中money不能是100
//请求地址:http://localhost:8080/quick
@RequestMapping("/quick")

2.5、组件扫描、Spring-MVC.xml配置解析

SpringMVc基于spring容器,所以在进行springMvc操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base-package=”com.itheima.controller"/>进行组件扫描。

注意要添加context命名空间

<!--组件扫描-->
<context:component-scan base-package="com.qin.controller"/>

一般可以指定一些组件,默认不配也可以;

springMVC的相关组件

  • 前端控制器:DispatcherServlet
  • 处理器映射器: HandlerMapping
  • 处理器适配器:HandlerAdapter
  • 处理器: Handler
  • 视图解析器: View Resolver
  • 视图: View

内部资源视图解析器

    <!--配置内部资源视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
  • REDIRECT_URL_PREFIX =“redirect:”-------重定向前缀;

  • FORWARD_URL_PREFIX = “forward:”--------转发前缀(默认值);

  • prefix = " "; -------视图名称前缀;

  • suffix = " "; -------视图名称后缀;

    redirect:重定向,地址栏信息产生变化;

    forward:转发,地址栏信息不会产生变化;

2.6、SpringMVC数据响应

  • 页面跳转

    • 直接返回字符串;此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转;

      返回带有前缀的字符串

      转发:fowward:/WEB-INF/views/index.jsp

      重定向:redirect:/index.jsp


    • 通过MonelAndView对象返回;

	@RequestMapping("/quick5")
    public String save5(HttpServletRequest request){
        //作用域
        request.setAttribute("user","不常用");
        return "success";
    }

    @RequestMapping("/quick4")
    public String save4(Model model){
        model.addAttribute("user","qinpeng");
        return "success";
    }

    @RequestMapping("/quick3")
    public ModelAndView save3(ModelAndView modelAndView){
        modelAndView.addObject("user","itcast");
        modelAndView.setViewName("success");
        return modelAndView;
    }

    @RequestMapping("/quick2")
    public ModelAndView save2(){
        /**
         * Model:模型,作用封装数据
         * View:视图,作用展示数据
         */
        ModelAndView modelAndView = new ModelAndView();
        //设置模型数据
        modelAndView.addObject("user","itcast");
        //设置视图名称
        modelAndView.setViewName("success");
        return modelAndView;
    }
  • 回写数据

    • 直接返回字符串;
    1. 通过SpringMVC框架注入的response对象,使用response.getWriter0.print(“hello world”)回写数
      据,此时不需要视图跳转,业务方法返回值为void。
    	@RequestMapping("/quick6")
        public void save6(HttpServletResponse response) throws Exception {
            response.getWriter().write("hello response!");
    
        }
    
    1. 将需要回写的字符串直接返回,但此时需要通过**@ResponseBody**注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。
    	@RequestMapping("/quick7")
        @ResponseBody  //告知SpringMVC框架,不进行视图跳转,直接进行数据响应;
        public String save7()  {
            return "hello ResponseBody!";
    
        }
    
    1. 返回json格式的字符串;
    		//转json需要的依赖
    		<dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>2.10.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.10.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.10.1</version>
            </dependency>
    
    	@RequestMapping("/quick9")
        @ResponseBody
        public String save9() throws Exception {
            User user = new User();
            user.setUsername("lisi");
            user.setAge(18);
    
            //使用json的转换工具,将对象转换成json格式的字符串再返回;
            ObjectMapper objectMapper = new ObjectMapper();
            String json = objectMapper.writeValueAsString(user);
    
            return json;
        }
    
    • 返回对象或集合;
      1. SpringMVC自动将数据转换成json格式,需要在spring-mvc.xml中配置
	<!--配置处理器映射器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            </list>
        </property>
    </bean>
	@RequestMapping("/quick00")
    @ResponseBody //使用springMVC自动将user转换成json格式
    public User save00() throws Exception {
        User user = new User();
        user.setUsername("lisi");
        user.setAge(18);

        return user;
    }

​ 2. 上面这种方式,比较复杂;配置代码较多,因此,我们可以使用mvc的注解代替上面的配置;记得要开启mvc命名空间

	<!--mvc的注解驱动-->
    <mvc:annotation-driven/>

注意:同时使用这个注解驱动时,默认底层就会集成jackson进行对象或集合的json格式字符串的转换;上面mvc的注解可以代替SpringMVC的三大组件;

2.7、SpringMVC获得请求参数

1、请求参数类型;

  • 基本类型参数;

Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配;直接在地址栏写数据,然后会打印到控制台;

	@RequestMapping("/quick01")
    @ResponseBody
    public void save01(String username,int age) throws Exception {
        System.out.println(username);
        System.out.println(age);
    }
  • POJO类型参数;

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配;直接在地址栏写数据,会自动封装成一个集合,然后会打印到控制台;

	//POJO数据类型(封装一个实体)
    @RequestMapping("/quick02")
    @ResponseBody
    public void save02(User user){
        System.out.println(user);
    }
  • 数组类型参数;

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配;直接在地址栏写数据,会自动封装成一个数组,然后会打印到控制台;

	//数组数据类型
    @RequestMapping("/quick03")
    @ResponseBody
    public void save03(String[] strs){
        System.out.println(Arrays.asList(strs));
    }
  • 集合类型参数;
  1. 获得集合参数,要将集合参数包装到一个POJO中才可以;;例如:先创建一个Vo类;在类中写集合;然后写一个提交表单,测试;
public class Vo {

    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    @Override
    public String toString() {
        return "Vo{" +
                "userList=" + userList +
                '}';
    }
}
<html>
<head>
    <title>集合参数</title>
</head>
<body>
    <form action="/quick04" method="post">
        <p>
            <%--
                userList[0].username
                集合的下标[0]...;后面的username就是集合里面传的实体里有的东西
            --%>
            <input type="text" name="userList[0].username"/><br>
            <input type="text" name="userList[0].age"/><br>
        </p>
        <p>
            <input type="text" name="userList[1].username"/><br>
            <input type="text" name="userList[1].age"/><br>
        </p>
        .........
        <input type="submit"/>
    </form>
</body>
</html>
	//集合类型
    @RequestMapping("/quick04")
    @ResponseBody
    public void save04(Vo vo){
        System.out.println(vo);
    }
  1. 当使用ajax提交时,可以指定cntentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装;记得开启静态资源访问;
    //集合类型2.0(ajax)
    @RequestMapping("/quick05")
    @ResponseBody
    public void save05(@RequestBody List<User> userList){
        System.out.println(userList);
    }
//模拟数据,写一个ajax请求
<html>
<head>
    <title>Title</title>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js">
    </script>
    <script>

        //模拟数据
        var userList = new Array();
        userList.push({username:"lisi",age:18});
        userList.push({username:"zhaoliu",age:99});

        $.ajax({
            type:"POST",
            url:"${pageContext.request.contextPath}/quick05",
            data:JSON.stringify(userList),
            contentType:"application/json;charset=utf-8"
        });

    </script>
</head>
<body>

</body>
</html>

在spring-mvc.xml中开启静态资源访问

	<!--静态资源访问开启-->
    <mvc:default-servlet-handler/>

2、配置全局乱码过滤器

在web.xml中进行配置

	<!--配置全局乱码过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3、参数绑定注解@RequestParam

当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定

    //参数绑定@RequestParam("xxx")
    @RequestMapping("/quick06")
    @ResponseBody
    public void save06(@RequestParam("name") String username, int age) throws Exception {
        System.out.println(username);
        System.out.println(age);
    }
http://localhost:8080/quick06?name=ls&age=10

注解@RequestParam还有如下参数可以使用:

  • value: 与请求参数名称;
  • required: 此在指定的请求参数是否必须包括,默认是rue,提交时如果没有此参数则报错;
  • defaultValue: 当没有指定请求参数时,则使用指定的默认值赋值;

4、获得Restful风格的参数

  • Restful是一种软件架构风格设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

  • Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下

    • GET:用于获取资源
    • POST:用于新建资源
    • PUT:用于更新资源
    • DELETE:用于删除资源
  • 例子:

    • /user/1 GET : 得到id =1的user
    • /user/1 DELETE: 删除id =1的 user
    • /user/1PUT: 更新id=1的user
    • /user POST: 新增user

    ur地址/user/1中的1就是要获得的请求参数,在SprinaMVC中可以使用占位符进行参数绑定。地址/ser/1可以写成user/id,占位符id;对应的就是1的值。在业务方法中我们可以使用**@PathVariable注解进行占位符的匹配获取工作;**

	//Restful风格
    //http://localhost:8080/quick07/zhangsan
    /**
     * 地址栏的zhangsan会传给@PathVariable("username"),然后在传给定义的username,打印到控制台
     * @param username
     */
    @RequestMapping("/quick07/{username}")
    @ResponseBody
    public void save07(@PathVariable("username") String username){
        System.out.println(username);
    }

5、自定义类型转换器

  • SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置;
  • 但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

自定义类型转换器的开发步骤;

  • 定义转换器类实现**Converter**接口;(日期类型转换器)
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String dateStr) {
        //将日期的字符串,转换成日期对象,返回
        SimpleDateFormat format = new SimpleDateFormat("yyy-MM-dd");
        Date date = null;
        try {
            date = format.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return date;
    }
}

  • 在spring-mvc.xml配置文件中,声明转换器;
    <!--声明转换器-->
    <bean id="conversionService" 	class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.qin.converter.DateConverter"></bean>
            </list>
        </property>
    </bean>
  • 在annotation-driven 中引用转换器;
    <!--mvc的注解驱动-->
    <mvc:annotation-driven conversion-service="conversionService"/>

6、获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下;

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
	@RequestMapping("/quick09")
    @ResponseBody
    public void save09(HttpServletRequest request, HttpServletResponse response, HttpSession session){
        System.out.println(request);
        System.out.println(response);
        System.out.println(session);
        
    }

7、获得请求头的数据

  • @RequestHeader
    • 使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name);@RequestHeader注解的属性如下:
    • value: 请求头的名称
    • required: 是否必须携带此请求头
	//获得请求头数据
    @RequestMapping("/quick10")
    @ResponseBody
    public void save10(@RequestHeader(value = "User-Agent",required = false) String user_agent){
        //把value的值赋值给user_agent
        System.out.println(user_agent);
    }
  • CookieValue
    • 使用@CookieValue可以获得指定Cookie的值@CookieValue注解的属性如下
    • value: 指定cookie的名称
    • required: 是否必须携带此cookie
    //获得请求头数据
    @RequestMapping("/quick11")
    @ResponseBody
    public void save11(@CookieValue(value = "JSESSIONID",required = false) String jsessionId){
        //把value的值赋值给jsessionId
        //JSESSIONID是键值对模式
        System.out.println(jsessionId);
    }

8、文件上传

  • 文件上传客户端三要素;
    • 表单项type=“ file ";
    • 表单的提交方式是post;
    • 表单的enctype属性是多部分表单形式,及enctype=“multipart/fokn-data;
<form action="/quick12" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"><br>
    文件:<input type="file" name="upload"><br>
    <input type="submit" value="提交">
</form>
  • 文件上传原理;

    • 当form表单修改为多部分表单时,requestgetParameter0将失效;
    • enctype=“application/x-www-form-urlencoded”时,form表单的正文内容格式是:key=value&key=value&key=value;
    • 当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成多部分形式;
  • 单文件上传步骤;

  1. 导入依赖;
		<dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
  1. 在spring-mvc.xml中进行配置;
<!--配置文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        //编码格式为UTF-8
        <property name="defaultEncoding" value="UTF-8"/>
        //文件上传大小为 5MB
        <property name="maxUploadSize" value="500000"/>
    </bean>
  1. 编写controller测试;
	//单文件上传
    @RequestMapping("/quick12")
    @ResponseBody
    public void save12(String username, MultipartFile uploadFile) throws IOException {
        System.out.println(username);
        System.out.println(uploadFile);

        //获得上传文件名称;
        String filename = uploadFile.getOriginalFilename();
        //文件上传位置;
        uploadFile.transferTo(new File("D:\\itcast\\"+filename));
    }
  • 多文件上传;

就是把形参的MultipartFile改成MultipartFile[] ;然后调用for循坏;

<form action="/quick13" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"><br>
    文件1:<input type="file" name="uploadFile"><br>
    文件2:<input type="file" name="uploadFile"><br>
    <input type="submit" value="提交">
</form>
    //多文件上传
    @RequestMapping("/quick13")
    @ResponseBody
    public void save13(String username, MultipartFile[] uploadFile) throws IOException {
        System.out.println(username);

        for (MultipartFile multipartFile : uploadFile) {
            //获得上传文件名称;
            String filename = multipartFile.getOriginalFilename();
            //文件上传位置;
            multipartFile.transferTo(new File("D:\\itcast\\"+filename));
        }

    }

3、JDBCTemplate

1、基本使用

开发步骤;

  • 导入依赖;
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
  • 创建数据库和实体类;
namevarchar(32)
moneydouble
public class Account {

    private String name;
    private double money;
    
    省略get...set...方法;还有toString方法
}
  • 测试;
	/**
     * 测试jdbcTemplate开发步骤
     * @throws Exception
     */
    @Test
    public void test() throws Exception {

        //创建数据源
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.jdbc.mysql.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUser("root");
        dataSource.setPassword("123456");

        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //设置数据源对象
        jdbcTemplate.setDataSource(dataSource);

        //执行操作
        int row = jdbcTemplate.update("insert into account values (?,?)", "tom", 5000);
        System.out.println(row);
    }

2、Spring产生jdbcTemplate对象

  • 在applicationContext.xml中进行配置;
    <!--加载外部的jdbc.properties-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--设置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--jdbc模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.user=root
jdbc.password=123456
  • 测试;
	/**
     * 测试spring产生jdbcTemplate对象
     * @throws Exception
     */
    @Test
    public void test2() throws Exception {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);

        //执行操作
        int row = jdbcTemplate.update("insert into account values (?,?)", "lisi", 5000);
        System.out.println(row);
    }

3、jdbcTemplate的CRUD

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class JdbcTemplateCRUDTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    
    /**
     * 修改
     */
    @Test
    public void testUpdate(){
        jdbcTemplate.update("update account set money=? where name=?",10000,"tom");
    }

    /**
     * 删除
     */
    @Test
    public void testDelete(){
        jdbcTemplate.update("delete from account where name=?","lisi");
    }

    /**
     * 查询全部
     */
    @Test
    public void testQueryAll(){
        String sql = "select * from account";
        /**
         * new BeanPropertyRowMapper<Account>(Account.class)
         * < >里面填写实体类,( )填写实体类的字节码,就是.class
         */
        List<Account> accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));

        //打印输出
        System.out.println(accountList);
    }

    /**
     * 查询单个对象
     */
    @Test
    public void testQueryOne(){
        //第一个值填写sql语句;第二个值填写new BeanPropertyRowMapper<>();第三个值填写要查询的对象;
        Account accountOne = jdbcTemplate.queryForObject("select * from account where name=?",
                                       new BeanPropertyRowMapper<Account>(Account.class),"tom");

        System.out.println(accountOne);
    }

    /**
     * 聚合查询
     */
    @Test
    public void testQueryCount(){
        //查询总条数
        Long aLong = jdbcTemplate.queryForObject("select count(*) from account ", Long.class);

        System.out.println(aLong);
    }


}

4、SpringMVC拦截器

1、拦截器的作用;

  • Spring MVC的拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理后处理
  • 将拦截器按一定的顺序联结成一条链,这条链称为==拦截器链(nterceptor Chain)==。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现;

2、拦截器和过滤器的区别

区别过滤器(Filter)拦截器(Interceptor)
使用范围是 servlet 规范中的一部分,任何Java Web 工程都可以使用;是 SpringMVC 框架自己的,只有使用了SpringMVC框架的工程才能用;
拦截范围在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截;在<mvc:mapping path=“ ”/>中配置了/**之后,也可以多所有资源进行拦截,但是可以通过<mvc;exclude-mapping path=“”/>标签排除不需要拦截的资源;

3、拦截器入门

  • 基础开发步骤
  1. 创建一个类实现HandlerInterceptor接口,重写里面的三个方法;
public class MyInterceptor implements HandlerInterceptor {

    //目标方法执行之前执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");
		
        //布尔值如果是false,后面的就都不会执行;
        //如果返回true,代表放行;
        //如果返回false,代表不放行;
        return true;
    }

    //目标方法执行之后,视图返回之前
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle.....");
    }

    //所有流程都执行完毕后再执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}
  1. 在spring-mvc.xml中进行配置;
    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--对那些资源执行拦截操作-->
            <mvc:mapping path="/**"/>
            <bean class="com.qin.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
方法名说明
preHandle()方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的当它返回为false时,表示请求结束,后续的Interceptor和Controller 都不会再执行;当返回值为true 时就会继续调用下一个nterceptor的preHandle方法
postHandle()该方法是在当前请求进行处理之后被调用,前提是preHandle 方法的返回值为true 时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView 对象进行操作;
afterCompletion()该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true 时才能被调用;

5、SpringMVC异常

系统中异常包括两类: 预期异常运行时异常RuntimeException , 前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生;

  • 系统的Dao、Service、Controller出现都通过throwsException向上抛出,最后由StringMVC前端控制器由异常处理器进行异常处理;

在这里插入图片描述

  • 异常处理的两种方式;

    • 使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver

    在spring-mvc.xml中进行配置;

    	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
            <!--默认的
    			value指定是视图 xxx.jsp
    		-->
            <property name="defaultErrorView" value="error"/>
            <!--自定义的异常-->
            <property name="exceptionMappings">
                <map>
                    <!--key表示自定义的异常-->
                    <entry key="java.lang.ClassCastException" value="error1"/>
                </map>
            </property>
        </bean>
    
    • 实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器;
    1. 创建异常处理类实现HandlerExceptionResolver接口,重写里面的方法;
public class MyExceptionResolver implements HandlerExceptionResolver {

    /**
     * 参数Exception :异常对象;
     * 返回值ModelAndView,跳到错误视图信息;
     *
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        ModelAndView modelAndView = new ModelAndView();

        if (e instanceof MyException){
            modelAndView.addObject("info","自定义异常!!!");
        } else if (e instanceof MyException){
            modelAndView.addObject("info","类型转换异常!!!");
        }

        modelAndView.setViewName("error");

        return modelAndView;
    }
}
  1. 在spring-mvc.xml进行bean的配置;
<bean class="com.qin.MyExceptionResolver"/>

6、Spring的AOP

1、简介

AOPAspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

  • AOPOOP的延续,是软件开发中的一个热点,也是Spring架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率;

AOP的作用与优势

  • 作用
    • 在程序运行期间,在不修改源码的情况下对方法进行功能增强;
  • 优势:
    • 减少重复代码,提高开发效率,并且便于维护;

2、AOP的动态代理技术

  • JDK代理:基于接口的动态代理技术;
/**
底层写法
*/
public class ProxyTest {
    public static void main(String[] args) {

        //目标对象
        final Target target = new Target();

        //增强对象
       final  Advice advice = new Advice();
       TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象类加载器;
                target.getClass().getInterfaces(),  //目标对象相同的接口字节码对象数组;
                new InvocationHandler() {
                    //调用代理对象的任何方法,实质执行的都是invoke方法
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前置增强;
                        advice.before();
                        //执行目标方法;
                        Object invoke = method.invoke(target, args);
                        //后置增强;
                        advice.afterReturning();
                        return invoke;
                    }
                }
        );
        proxy.save();

    }
}
  • cglib代理:基于父类的动态代理技术;不需要接口;
/**
底层写法
*/
public class ProxyTest {
    public static void main(final String[] args) {

        //目标对象
        final Target target = new Target();

        //增强对象
        final Advice advice = new Advice();

        //返回值;就是动态生成的代理对象;基于cglib
        //1.创建增强器
        Enhancer enhancer = new Enhancer();
        //2.设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3.设置回调
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //执行前置
                advice.before();
                //执行目标
                Object invoke = method.invoke(target, args);
                //执行后置;
                advice.afterReturning();
                return invoke;
            }
        });

        //创建代理对象
        Target proxy = (Target) enhancer.create();


        proxy.save();

    }
}

3、AOP的相关概念

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

3.1、AOP相关的术语

  • Target (目标对象):代理的目标对象;
  • Proxy (代理):一个类被AOP织入增强后,就产生一个结果代理类;
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点;
  • Pointcut (切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义;
  • Advice (通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知;
  • Aspect (切面): 是切入点和通知()的结合;
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类载期织入;

3.2、AOP开发的事项

  • 需要编写的内容;

    • 编写核心业务代码(目标类的目标方法);
    • 编写切面类,切面类中有通知(增强功能方法);
    • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合;
  • AOP技术实现的内容;

    • Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
  • AOP底层会使用那种代理方式;

    • 在spring中,框架会根据目标类是否实现了接口来决定采用那种动态代理的方式;

4、基于XML方式实现AOP

  1. 导入依赖;
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.4</version>
    </dependency>
  1. 编写目标接口,目标接口的实现类,增强类(before…);
public interface TargetInterface {
    void save();
}
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running....");
    }
}
public class MyAspect {

    public void before(){
        System.out.println("前置...");
    }

}
  1. 在applicationContext.xml进行配置;
    <!--配置目标对象-->
    <bean id="target" class="com.qin.aop.Target"></bean>

    <!--配置切面-->
    <bean id="myAspect" class="com.qin.aop.MyAspect"></bean>

    <!--配置织入;告诉spring框架,那些方法(切点)需要进行增强(前置...;后置...)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面:切点+通知 -->
            <aop:before method="before" pointcut="execution(public void com.qin.aop.Target.save())"/>
        </aop:aspect>
    </aop:config>
  1. 测试;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Autowired
    private TargetInterface targetInterface;

    @Test
    public void test1(){
        targetInterface.save();
    }
}

1、切点表达式的写法

表达式的语法

execution([修饰符]返回值类型包名类名.方法名(参数))
  • 访问修饰符可以省略;
  • 返回值类型、包名、类名、方法名可以使用星号*代表任意;
  • 包名与类名之间一个点.代表当前包下的类,两个点.表示当前包及其子包下的类;
  • 参数列表可以使用两个点…表示任意个数,任意类型的参数列表;

切点表达式的抽取:
当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref属性代替 pointcut属性来引用抽取后的切点表达式;

	<aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切点表达式的抽取-->
            <aop:pointcut id="myPointcut" expression="execution(* com.qin.aop.*.*(..))"/>
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

2、通知的类型

  • 通知的配置语法;
<aop:通知类型 method=“切面类中方法名”pointcut=“切点表达式”></aop:通知类型>
名称标签说明
前置通知aop:before用于配置前置通知。指定增强的方法在切入点方法之前执行;
后置通知aop:after-returning用于配置后置通知。指定增强的方法在切入点方法之后执行;
环绕通知aop:around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行;
异常抛出通知aop:throwing用于配置异常抛出通知。指定增强的方法在出现异常时执行;
最终通知aop:after用于配置最终通知。无论增强方式执行是否有异常都会执行;
public class MyAspect {

    public void before(){
        System.out.println("前置...");
    }

    public void afterReturning(){
        System.out.println("后置...");
    }

    /**
     * ProceedingJoinPoint 正在执行的连接点===切点
     *
     * 原始方法如果是 void 类型,可以设置成void或者Object;
     * @param pjp
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前通知...");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后通知...");
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("异常抛出通知...");
    }

    public void after(){
        System.out.println("最终通知...");
    }
}
    <!--配置织入;告诉spring框架,那些方法(切点)需要进行增强(前置...;后置...)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切点表达式的抽取-->
            <aop:pointcut id="myPointcut" expression="execution(* com.qin.aop.*.*(..))"/>
            <!--切面:切点+通知-->
            <!--<aop:before method="before" pointcut="execution(public void com.qin.aop.Target.save())"/>-->
            <!--<aop:before method="before" pointcut="execution(* com.qin.aop.*.*(..))"/>-->
            <!--<aop:after-returning method="afterReturning" pointcut="execution(* com.qin.aop.*.*(..))"/>-->
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>

5、基于注解方式实现AOP

  1. 切面类用==@Aspect标识;添加@Component(“xxx”)==注解;通知方法用注解来安排;@Before()…
@Aspect //标注当前MyAspect是一个切面类
@Component("myAspect")
public class MyAspect {

    @Before("execution(* com.qin.annoAOP.*.*(..))")
    public void before(){
        System.out.println("前置...");
    }

    @AfterReturning("execution(* com.qin.annoAOP.*.*(..))")
    public void afterReturning(){
        System.out.println("后置...");
    }
}
  1. 在目标类的实现类中加注解@Component(“xxx”);
@Component("target")
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running....");
    }
}
  1. 在applicationContext中进行配置;
    <!--开启组件扫描-->
    <context:component-scan base-package="com.qin.annoAOP"/>

    <!--aop自动代理-->
    <aop:aspectj-autoproxy/>
  1. 测试;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class annoAopTest {

    @Autowired
    private TargetInterface targetInterface;

    @Test
    public void test1(){
        targetInterface.save();
    }
}

1、切点表达式

同xml配置aop 一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut;注解定义切点表达式,然后在在增强注解中进行引用。(在切面类中定义一个方法)

	//第一种写法;
	@After("pointcut()")
    public void after(){
        System.out.println("最终通知...");
    }

	//第二种写法;
	@After("MyAspect.pointcut()")
    public void after(){
        System.out.println("最终通知...");
    }

    //定义切点表达式
    @Pointcut("execution(* com.qin.annoAOP.*.*(..))")
    public void pointcut(){}
  • 注解aop开发步骤
    • 使用@Aspect标注切面类;
    • 使用@通知注解标注通知方法;
    • 在配置文件中配置aop自动代理aop:aspectj-autoproxy/
前置通知@Before用于配置前置通知。指定增强的方法在切入点方法之前执行;
后置通知@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行;
环绕通知@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行;
异常抛出通知@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行;
最终通知@After用于配置最终通知。无论增强方式执行是否有异常都会执行;

7、Spring的事务控制

1、编程式事务

  • 编程式相关的对象

编程式事务控制的三大对象:

  1. PlatformTransactionManager接口是spring 的事务管理器,它里面提供了我们常用的操作事务的方法;
方法说明
Transactionstatus
getTransaction(TransactionDefination defination)
获取事务的状态信息
void commit(TransactionStatus status)提交事务
void rollback(Transactionstatus status)回滚事务

注意:platformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如: Dao 层技术是idbc或mybatis 时: org.springframework.jdbc.datasource.DataSourceTransactionManagerDao 层技术是hibernate时: org.springframework.orm.hibernate5.HibernateTransactionManager;


  1. TransactionDefinition事务的定义信息对象,里面有如下方法;
方法说明
int getIsolationLevel()获得事务的隔离级别
int getPropogationBehavior ()获得事务的传播行为
int getTimeout()获得超时时间
boolean isReadonly()是否只读

2.1、事务的隔离级别;【设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读;】

  • ISOLATION DEFAULT
  • ISOLATION READ UNCOMMITTED
  • ISOLATION READ COMMITTED
  • ISOLATION REPEATABLE READ
  • ISOLATION SERIALIZABLE

2.2、事务的传播行为;

  • REQUIRED: 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值);
  • SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务);
  • MANDATORY: 使用当前的事务,如果当前没有事务,就抛出异常;
  • REQUERSNEW: 新建事务,如果当前在事务中,把当前事务挂起;
  • NOT SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;
  • NEVER: 以非事务方式运行,如果当前存在事务,抛出异常;
  • NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED 类似的操作;
  • 超时时间: 默认值是-1,没有超时限制。如果有,以秒为单位进行设置;
  • 是否只读: 建议查询时设置为只读;

  1. TransactionStatus接口提供的是事务具体的运行状态;
方法说明
boolean hasSavepoint ()是否存储回滚点
boolean isCompleted()事务是否完成
boolean isNewTransaction()是否是新事务
boolean isRollbackonly()事务是否回滚

2、声明式事务

​ Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务;

声明式事务的作用;

  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话此,也只需要在定义文件中重新配置即可;
  • 在不需要事务管理的时候只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译这样维护起来极其方便;

注意:Spring声明式事务控制底层就是AOP

3、基于xml的声明式事务控制

    <!--1.配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--2.通知 增强的事务-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--设置事务的属性信息-->
        <tx:attributes>
<!-- <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" />-->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--3.配置事务的aop织入-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.qin.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
    </aop:config>

其中,tx:method代表切点方法的事务参数的配置;

<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" />
  • name: 切点方法名称;
  • isolation: 事务的隔离级别;
  • propogation: 事务的传播行为;
  • timeout: 超时时间;
  • read-only: 是否只读;

4、基于注解的声明式事务控制

  • 在需要执行的目标方法上面添加**@Transactional**注解即可;
    • 使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如隔离级别、传播行为等。
    • 注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置;
    • 使用在方法上,不同的方法可以采用不同的事务参数配置;
    • Xml配置文件中要开启事务的注解驱动**<tx:annotation-driven />**
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Transactional
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan, money);
        int i = 1/0;
        accountDao.in(inMan, money);
    }
}

  • 在applicationContext.xml中进行配置;
	<!--组件扫描-->
    <context:component-scan base-package="com.qin"/>

	<!--1.配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--2.事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

8、Mybatis

  • mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程;
  • mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句;
  • 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对idbc进行了封装,屏蔽了idbcapi底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

1、开发步骤

  • 导入依赖;
	<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.32</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
  • 创建数据表和实体类;
  • 编写映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="userMapper">
    <select id="findAll" resultType="com.qin.domain.User">
        select * from user
    </select>
</mapper>

在这里插入图片描述

  • 编写核心文件
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--数据源环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!--加载映射文件 注意:是resource!!!-->
    <mappers>
        <mapper resource="com/qin/mapper/UserMapper.xml"></mapper>
    </mappers>

</configuration>
  • 编写测试类
    @Test
    public void test1() throws IOException {

        //获得核心配置文件
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");

        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

        //获得session的会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行操作 namespace + id
        List<Object> selectList = sqlSession.selectList("userMapper.findAll");

        //打印输出
        System.out.println(selectList);

        //释放资源
        sqlSession.close();
    }
  • 插入的注意事项;
    • 插入语句使用insert标签;
    • 在映射文件中使用pagmeterType属性指定要插入的数据类型;
    • Sql语句中使用#(实体属性名)方式引用实体中的属性值;
    • 插入操作使用的API是sqlSessioninsert(“命名空间.id”,实体对象);
    • 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务即sqlSession.commit()

  • 修改的注意事项;
    • 修改语句使用update标签;
    • 修改操作使用的API是sqlSessionupdate(“命名空间id”,实体对象);

  • 删除的注意事项;
    • 删除语句使用delete标签;
    • Sql语句中使用#(任意字符串)方式引用传递的单个参数;
    • 删除操作使用的API是sqlSession.delete(“命名空间id”,Object);

  • 简易的CRUD
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="userMapper">

    <!--查询操作-->
    <select id="findAll" resultType="com.qin.domain.User">
        select *
        from users
    </select>

    <!--插入操作-->
    <insert id="save" parameterType="com.qin.domain.User">
        insert into users
        values (#{id}, #{username}, #{password})
    </insert>

    <!--修改-->
    <update id="update" parameterType="com.qin.domain.User">
        update users
        set username=#{username},
            password=#{password}
        where id = #{id}
    </update>

    <!--删除-->
    <delete id="del" parameterType="java.lang.Integer">
        delete from users where id=#{id}
    </delete>

</mapper>
public class MyBatisTest {

    /**
     * 删除
     * @throws IOException
     */
    @Test
    public void del() throws IOException {

        //获得核心配置文件
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");

        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

        //获得session的会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行操作 namespace + id
        sqlSession.delete("userMapper.del",2);

        //事务提交
        sqlSession.commit();

        //释放资源
        sqlSession.close();

    }

    /**
     * 修改
     * @throws IOException
     */
    @Test
    public void update() throws IOException {

        //模拟数据
        User user = new User();
        user.setId(2);
        user.setUsername("tom");
        user.setPassword("123321123");

        //获得核心配置文件
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");

        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

        //获得session的会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行操作 namespace + id
        sqlSession.update("userMapper.update",user);

        //事务提交
        sqlSession.commit();

        //释放资源
        sqlSession.close();

    }

    /**
     * 插入
     * @throws IOException
     */
    @Test
    public void save() throws IOException {

        //模拟数据
        User user = new User();
        user.setUsername("lucy");
        user.setPassword("123456");

        //获得核心配置文件
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");

        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

        //获得session的会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行操作 namespace + id
        sqlSession.insert("userMapper.save", user);

        //事务提交
        sqlSession.commit();

        //释放资源
        sqlSession.close();

    }


    /**
     * 查询
     * @throws IOException
     */
    @Test
    public void test1() throws IOException {

        //获得核心配置文件
        InputStream resource = Resources.getResourceAsStream("sqlMapConfig.xml");

        //获得session工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resource);

        //获得session的会话对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //执行操作 namespace + id
        List<Object> selectList = sqlSession.selectList("userMapper.findAll");

        //打印输出
        System.out.println(selectList);

        //释放资源
        sqlSession.close();
    }

}

2、核心配置文件概述

1、MyBatis核心配置文件层级关系;

  • configuration 配置
    • properties 属性
    • settings 设置
    • typeAliases 类型别名
    • typeHandlers 类型处理器
    • objectFactory 对象工厂
    • plugins 插件
    • environments 环境
      • environment 环境变量
        • transactionManager 事务管理器
        • dataSource数据源
    • databaseldProvider 数据库厂商标识
    • moppers 映射器

2、environments标签;

在这里插入图片描述

  • 事务管理器 (transactionManager) 类型有两种:
    • **JDBC:**这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
    • **MANAGED:**这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将loseConnection属性设置为false来阻止它默认的关闭行为。
  • 数据源(dataSource) 类型有三种:
    • **UNPOOLED:**这个数据源的实现只是每次被请求时打开和关闭连接;
    • **POOLED:**这种数据源的实现利用“池”的概念将JDBC连接对象组织起来;
    • **JNDl:**这个数据源的实现是为了能在如EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。

3、mapper标签;

  • 该标签的作用是加载映射的,加载方式有如下几种:
    • 使用相对于类路径的资源引用,例如:
    • 使用完全限定资源定位符(URL),例如:
    • 使用映射器接口突现类的完全限定类名,例如: <mapper class="org.mybatis.builder.AuthorMapper”/>
    • 将包内的映射器接口实现全部注册为映射器,例如:<package name="org.mybatis.builder”/>

4、Properties标签;

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件;

	<!--加载外部的jdbc.properties-->
    <properties resource="jdbc.properties"></properties>

5、typeAliases标签;

  • 自定义别名;【注意放的位置,要上在environments标签上面;】
 	<!--自定义别名-->
    <typeAliases>
        <typeAlias type="com.qin.domain.User" alias="user"></typeAlias>
    </typeAliases>
  • mybatis框架为我们设置好的常用类型别名;
别名数据类型
stringString
longLong
intInteger
doubleDouble
booleanBoolbean

6、typeHandlers标签;

  • 无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java 类型;

  • 可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatistype.TypeHandler接口,或继承一个很的类org.apacheibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型;

  • 开发步骤;(date类型转换)

    • 定义转换类继承类BaseTypeHandler
    • 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java的Type类型的方法
    public class DateHandler extends BaseTypeHandler<Date> {
    
        //将java类型,转换成数据库需要的类型;
        public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
            long time = date.getTime();
            preparedStatement.setObject(i,time);
        }
    
        //将数据库中类型,转换成java类型
        //String参数,要转换的字段名称
        //resultSet 查询出的结果集
        public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
            //获得结果集需要的数据(long),转换成date类型,返回
            long aLong = resultSet.getLong(s);
            Date date = new Date(aLong);
            return date;
        }
    
        //将数据库中类型,转换成java类型
        public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
            long aLong = resultSet.getLong(i);
            Date date = new Date(aLong);
            return date;
        }
    
        //将数据库中类型,转换成java类型
        public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
            long aLong = callableStatement.getLong(i);
            Date date = new Date(aLong);
            return date;
        }
    }
    
    • 在MyBatis核心配置文件中进行注册;
    • 在核心配置文件中进行配置;【注意位置,放到environments标签上面!!!】
    <!--注册自定义类型处理器-->
    <typeHandlers>
        <typeHandler handler="com.qin.handler.DateHandler"></typeHandler>
    </typeHandlers>

7、Plugins标签

​ MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据;

  • 开发步骤;
  1. 导入依赖;
    <dependency>
      <groupId>com.github.jsqlparser</groupId>
      <artifactId>jsqlparser</artifactId>
      <version>0.9.1</version>
    </dependency>

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>3.7.5</version>
    </dependency>
  1. 在核心配置文件中配置插件助手;【注意位置:放到environments标签上面!!!】
	<!--配置分页助手插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>
  1. 测试;
@Test
public void test3() throws IOException {

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserDao mapper = sqlSession.getMapper(UserDao.class);

        //设置分页相关参数,当前页+每页显示的条数
        //当前为第一页,显示三条
        PageHelper.startPage(1,3);

        List<User> all = mapper.findAll();

        for (User user : all) {
            System.out.println(user);
        }

        sqlSession.close();

    }
  1. 分页助手,相关的分页数据;
        List<User> all = mapper.findAll();

        for (User user : all) {
            System.out.println(user);
        }

        //获得与分页相关的参数
        PageInfo<User> pageInfo = new PageInfo<User>(all);
        System.out.println("当前页:" + pageInfo.getPageNum());
        System.out.println("每页显示条数:" + pageInfo.getPageSize());
        System.out.println("总条数:" + pageInfo.getTotal());
        System.out.println("总页数:" + pageInfo.getPages());
        System.out.println("上一页:" + pageInfo.getPrePage());
        System.out.println("下一页:" + pageInfo.getNextPage());
        System.out.println("是否是第一个:" + pageInfo.isIsFirstPage());
        System.out.println("是否是最后一个:" + pageInfo.isIsLastPage());

3、相关API

  • SqlSession工厂对象SqlSessionFactory
    • SqlSessionFactory有多个个方法创建SqlSession 实例。常用的有如下两个:
方法解释
openSession0会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中
openSession(booleanautoCommit)参数为是否自动提交,如果设置为true,那么不需要手动提交事务
  • SqlSession会话对象;

    • Sqlsesion 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法执行语句的方法主要有:
    T selectOne(string statement, Object parameter)
    <E> List <E> selectList(String statement, bject parameter)
    int insert(String statement,Object parameter)
    int update(stringstatement,Object parameter )
    int delete(String statement, Object parameter)
    
    • 操作事务的方法主要有:
void commit()
void rollback()

4、MyBatis的dao层实现

  1. 代理开发方式;重点
  • Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法;

Mapper开发需要遵循以下规范:

  • Mapper.xml文件中的namespace与mapper接口的全限定名相同;
  • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同;
  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
public interface UserDao {

    List<User> findAll() throws IOException;

}
<mapper namespace="com.qin.dao.UserDao">

    <!--查询操作-->
    <select id="findAll" resultType="user">
        select *
        from users
    </select>


</mapper>

public class ServiceDemo {

    public static void main(String[] args) throws IOException {
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDao mapper = sqlSession.getMapper(UserDao.class);

        List<User> all = mapper.findAll();
        System.out.println(all);

    }

}
  1. 传统方式:手动对dao层进行实现;

5、动态sql

  1. 标签【判断】
    <select id="findByCondition" parameterType="user" resultType="user">
        select *
        from user
        <where>
            <if test="id!=0">
                id = #{id}
            </if>
            <if test="username!=null">
                and username = #{username}
            </if>
            <if test="password!=null">
                and password = #{password}
            </if>
        </where>
    </select>
    @Test
    public void test1() throws IOException {

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserDao mapper = sqlSession.getMapper(UserDao.class);

        //模拟数据
        User user = new User();
        user.setId(1);
//        user.setUsername("lisi");
//        user.setPassword("123");

        List<User> userList = mapper.findByCondition(user);

        System.out.println(userList);
  1. 标签【循环】
    <select id="findByIds" resultType="user" parameterType="list">
        select * from user
        <where>
            <foreach collection="list" open="id in(" close=")" item="id" separator=",">
                #{id}
            </foreach>
        </where>
    </select>
    @Test
    public void test2() throws IOException {

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("sqlMapConfig.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession();

        UserDao mapper = sqlSession.getMapper(UserDao.class);

        ArrayList<Integer> ids = new ArrayList<Integer>();
        ids.add(1);

        List<User> byIds = mapper.findByIds(ids);

        System.out.println(ids);


    }
  1. sql片段的抽取;

在映射文件中定义:

	<!--sql语句的抽取-->
    <sql id="selectUser">select * from user</sql>
	/*引用sql语句*/
    <include refid="selectUser"></include>

6、Mybatis多表操作

一对一、一对多(多对一)、多对多

    <!--一对一的写法-->
	<resultMap id="orderMap" type="order">

        <!--手动指定字段与实体属性的映射关系
            column: 数据表的字段名称
            property:实体的属性名称
        -->
        <id column="oid" property="id"></id>
        <result column="ordertime" property="ordertime"></result>
        <result column="total" property="total"></result>
<!--        <result column="uid" property="user.id"></result>
        <result column="username" property="user.username"></result>
        <result column="password" property="user.password"></result>
        <result column="birthday" property="user.birthday"></result>-->

        <!--
            property: 当前实体(order)中的属性名称(private User user)
            javaType: 当前实体(order)中的属性的类型(User)
        -->
        <association property="user" javaType="user">
            <id column="uid" property="id"></id>
            <result column="username" property="username"></result>
            <result column="password" property="password"></result>
            <result column="birthday" property="birthday"></result>
        </association>

    </resultMap>

    <select id="findAll" resultMap="orderMap">
        SELECT *,o.id oid FROM orders o,USER u WHERE o.uid=u.id
    </select>
  • Mybatis多表配置方式;
    • 一对一配置:使用做配置;
    • 一对多配置:使用+做配置;
    • 多对多配置:使用+做配置;

7、注解开发

Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了;

常用的注解:

  • @Insert: 实现新增;
  • @Update: 实现更新;
  • @Delete: 实现删除;
  • @Select: 实现查询;
  • @Result: 实现结果集封装;
  • @Results: 可以与@Resut一起使用,封装多个结果集;
  • @One:实现一对一结果集封装;
  • @Many: 实现一对多结果集封装;

  1. 用注解替代映射文件;
    @Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
    void save(User user);

    @Update("update user set username=#{username},password=#{password} where id =#{id}")
    void update(User user);

    @Delete("delete from user where id =#{id}")
    void del(int id);

    @Select("select * from user where id =#{id}")
    List<User> findById(int id);

    @Select("select * from user")
    List<User> findAll();
  1. 在核心配置中进行配置;
    <!--加载映射关系-->
    <mappers>
        <!--指定接口所在的包-->
        <package name="com.qin.mapper"/>
    </mappers>
  1. 测试无改变;

一对一的注解开发;

注解说明
@Results代替的是标签该注解中可以使用单个@Result注解,也可以使用@Result集合。使用格式: @Results (@Result () ,@Result () }) 或@Results (@Result () )
@Resut代替了标签和标签
@Result中属性介绍
column: 数据库的列名
property: 需要装配的属性名
one: 需要使用的@One 注解 (@Result (one= @One) () ) )
maky: 需要使用的@Many注解 (@Result (many=@many) () ) )

9、Mybatis整合spring

可以把以下配置移至spring的配置文件中;(applicationContext.xml);

	<!--加载外部的jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置sqlSessionFactory-->
    <bean id="SqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--加载mybatis核心配置文件-->
        <property name="configLocation" value="classpath:sqlMapConfig-spring.xml"/>
    </bean>

    <!--加载映射文件
        扫描mapper所在的包,为mapper创建实现类;
        业务层可以直接注入AccountMapper接口,然后调用方法;
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.qin.mapper"/>
    </bean>

    <!--声明式事务控制-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事务增强-->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--事务的aop织入-->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.qin.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>

Mybatis的核心配置文件;

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--设置别名-->
    <typeAliases>
        <typeAlias type="com.qin.domain.Account" alias="account"/>
    </typeAliases>


</configuration>

},#{birthday})")
void save(User user);

@Update("update user set username=#{username},password=#{password} where id =#{id}")
void update(User user);

@Delete("delete from user where id =#{id}")
void del(int id);

@Select("select * from user where id =#{id}")
List<User> findById(int id);

@Select("select * from user")
List<User> findAll();

2. 在核心配置中进行配置;

```xml
    <!--加载映射关系-->
    <mappers>
        <!--指定接口所在的包-->
        <package name="com.qin.mapper"/>
    </mappers>
  1. 测试无改变;

一对一的注解开发;

注解说明
@Results代替的是标签该注解中可以使用单个@Result注解,也可以使用@Result集合。使用格式: @Results (@Result () ,@Result () }) 或@Results (@Result () )
@Resut代替了标签和标签
@Result中属性介绍
column: 数据库的列名
property: 需要装配的属性名
one: 需要使用的@One 注解 (@Result (one= @One) () ) )
maky: 需要使用的@Many注解 (@Result (many=@many) () ) )

9、Mybatis整合spring

可以把以下配置移至spring的配置文件中;(applicationContext.xml);

	<!--加载外部的jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--配置sqlSessionFactory-->
    <bean id="SqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--加载mybatis核心配置文件-->
        <property name="configLocation" value="classpath:sqlMapConfig-spring.xml"/>
    </bean>

    <!--加载映射文件
        扫描mapper所在的包,为mapper创建实现类;
        业务层可以直接注入AccountMapper接口,然后调用方法;
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.qin.mapper"/>
    </bean>

    <!--声明式事务控制-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事务增强-->
    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!--事务的aop织入-->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.qin.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>

Mybatis的核心配置文件;

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--设置别名-->
    <typeAliases>
        <typeAlias type="com.qin.domain.Account" alias="account"/>
    </typeAliases>


</configuration>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值