依赖注解DI的实现方式
- (1)给成员变量赋值的方式有两种
- (2)1是有参构造方法
- (3)2是先通过无参构造方法创建对象,再调用set方法
- (4)依赖注入 简写DI 本质是 对成员变量赋值
- (5)可以使用xml配置也可以使用注解配置
- (6)xml可以使用constructor-arg标签,或property标签
public class ProductService implements IProductService{
//private IProductDao productDao = new ProductDao();//多态
private IProductDao productDao;
//构造方法
public ProductService(IProductDao productDao) {
this.productDao = productDao;
}
//无参构造方法
public ProductService() {
}
public void setProductDao(IProductDao productDao) {
this.productDao = productDao;
}
先用xml配置
<bean id="productDao" class="com.huawei.dao.ProductDao"></bean>
<bean id="productService" class="com.huawei.service.ProductService">
<constructor-arg name="productDao" ref="productDao"/>
</bean>
<bean id="productService2" class="com.huawei.service.ProductService">
<property name="productDao" ref="productDao"/>
</bean>
也可以使用注解配置
<!-- 扫描指定的目标,只要有@Service,@Repository @Controller @Component-->
<context:component-scan base-package="com.huawei"/>
@Service
public class ProductService implements IProductService{
//private IProductDao productDao = new ProductDao();//多态
@Autowired
private IProductDao productDao;
@Repository
public class ProductDao
依赖注解DI的配置数据源与JdbcTemplate
- (1)如果是调用别人定义的类,不能加注解的情况下,只能使用xml
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db_product"/>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
批量操作的事务问题
- (1)事务:一组操作要么全部成功,如果有一条失败就得回滚
- (2)事力处理一般需要在业务层编写事务代码
@Test
public void test01(){
//业务层需要处理事务,需要使用AOP
//事务:一组操作要么全部成功,如果有一条失败就得回滚
//转钱 -500 + 500
IProductService iProductService = (IProductService) context.getBean("productService");
iProductService.deleteProducts("1","2","3");
}
在ProductSerivice中
public void deleteProducts(String... ids) {
if (ids.length == 0){
return;
}
for (int i = 0; i < ids.length; i++) {
//模拟,删到第2条就抛出异常
if(i == 1){
System.out.println(1/0);
}
productDao.deleteById(ids[i]);
}
}
执行测试发现,第一条删除成功,第二条起失败,
但是数据没有回滚,原因是没有编写事务处理代码
静态代理添加事务代码
- (1)在不改变源代码的基础上对目标方法进行增强,有两种方式
- (2)1,是静态代码
- (3)2,是动态代理,又分JDK动态代理,CGlib动态代理
- (4)Spring AOP使用两种动态代理
- (5)静态代理,由开发者自己编写代理类,要求代理类实现与目标相同的接口,内部还是调用目标对象与增强对象
public class AccountDao {
public void update(double money){
System.out.println("update ..."+money);
}
}
public class MyTxManager {
public void begin(){
System.out.println("开启事务");
}
public void commit(){
System.out.println("提交事务");
}
public void rollback(){
System.out.println("回滚事务");
}
public void close(){
System.out.println("关闭事务");
}
}
//静态代理模式
//代理就是生成一个新类
//静态代理,由你来生成这个类
//实现相同的接口
//静态代理,比直接 改代码安全,但是方法多的话,工作量比较大
public class AccountServiceNew implements IAccountService{
private AccountService accountService=new AccountService();
private MyTxManager txManager=new MyTxManager();
public void zhuan() {
try {
txManager.begin();
accountService.zhuan();
txManager.commit();
} catch (Exception e) {
e.printStackTrace();
txManager.rollback();
} finally {
txManager.close();
}
}
@Test
public void test02(){
//IAccountService accountService = new AccountService();
IAccountService accountService = new AccountServiceNew();
accountService.zhuan();
}
执行结果中事务管理正常
静态代理的好处是,不改原代码,缺点就是编码量还是比较大
JDK动态代理添加事务代码
- (1)在不改变源代码的基础上对目标方法进行增强
- (2)动态代理的本质是生成一个新的类,叫代理类
- (3)代理类中与目标类实现相同的接口
- (4)代理类在运行时产生,在运行结束后销毁
@Test
public void test03(){//JDK
final AccountService accountService = new AccountService();//目标对象
final MyTxManager myTxManager = new MyTxManager();//通知对象
//代理类与代理对象
ClassLoader loader=accountService.getClass().getClassLoader();//参1 相同的加载器
Class<?>[] interfaces=accountService.getClass().getInterfaces();//参2 相同的接口
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//参3 逻辑
System.out.println("新类的对象 代理对象 "+proxy.getClass());
System.out.println("切点方法 "+method.getName());
System.out.println("切点参数 "+ Arrays.toString(args));
try {
myTxManager.begin();
Object result = method.invoke(accountService,args);
myTxManager.commit();
return result;
} catch (Exception e) {
e.printStackTrace();
myTxManager.rollback();
} finally {
myTxManager.close();
}
return null;
}
};
IAccountService accountService1= (IAccountService) Proxy.newProxyInstance(loader,interfaces,handler);
// accountService1.zhuan();
accountService1.zhuan2();
}
动态代理虽然也编写了不少代码,但是以后业务类方法增加,动态代理不需要再编写更多的事务管理的代码
比如业务类增加转账方法2
SpringAOP添加事务代码
- (1)AOP即面向切面编程
- (2)面向切面编程 底层执行的是动态代理
- (3)面向切面编程,分两步
- (4)1,使用切点表达式找出连接点中的切点方法
- (5)2,编写增强逻辑 比如事务 就编写try,catch,finally
》pom.xml设置依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
》applicationContext.xml设置 切面
<!-- 开始切面编程,确定切点,然后对切点进行 逻辑配置-->
<bean class="com.huawei.tx.AccountService" id="accountService">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean class="com.huawei.tx.AccountDao" id="accountDao"/>
<bean class="com.huawei.tx.MyTxManager" id="txManager"/>
<aop:config>
<!-- <aop:pointcut id="p1" expression="execution(修改符 返回值 方法名(参数))"/>-->
<aop:pointcut id="p1" expression="execution( * com.huawei.tx.*Service.*(..))"/>
<aop:aspect ref="txManager">
<!--四大位置-->
<aop:before method="begin" pointcut-ref="p1"/>
<aop:after-returning method="commit" pointcut-ref="p1"/>
<aop:after-throwing method="rollback" pointcut-ref="p1"/>
<aop:after method="close" pointcut-ref="p1"/>
</aop:aspect>
</aop:config>
》执行测试方法
@Test
public void test04(){
IAccountService accountService = (IAccountService) context.getBean("accountService");
System.out.println(accountService);
accountService.zhuan();
}
Spring声明式事务代码
- (1)Spring的声明式事务管理是通过SpringAOP实现的
最符合 非侵入式 轻量级容器的理念
》applicationContext.xml中设置切面
<!-- spring的声明式事务-->
<bean id="tm"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Spring的事务是基于AOP,但是为了简化则采用样板风格,此处仅仅配置哪些方法名称需要切入事务-->
<tx:advice id="advice" transaction-manager="tm">
<tx:attributes>
<!-- 有就用,没有就创建一个新事务-->
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<!-- get query-->
<tx:method name="find*" propagation="NEVER"/>
<tx:method name="get*" propagation="NEVER"/>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.huawei.service..*Service.*(..))"
id="pointcut"/>
<!--pointcut:切面表达式:指定包、类名称+tx 则是完整切面路径 -->
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>
》运行测试方法
@Test
public void test01(){
//业务层需要处理事务,需要使用AOP
//事务:一组操作要么全部成功,如果有一条失败就得回滚
//转钱 -500 + 500
IProductService iProductService = (IProductService) context.getBean("productService");
iProductService.deleteProducts("1","2","3");
}
SpringMVC介绍
-
(1)什么是 springmvc?
Spring mvc属于表现层的框架,它是Spring框架的一部分 -
(2)SpringMVC的作用是?
》接收请求,获取参数
》处理参数
》将结果响应给浏览器 如 重定向或者请求转发或者返回json
SpringMVC的流程
- (1)什么是 springmvc的工作流程?
- (2)前端控制器将请求转给Controller
- (3)Controller 处理三件事, 获取参数,处理参数,将结果返回给浏览器
SpringMVC环境搭建
- (1)设置pom.xml依赖
- (2)设置tomcat
- (3)设置web.xml
- (4)设置web.xml 中加载springmvc.xml
- (5)设置web.xml 中加载applicaitonContext.xml
<!-- springmvc 1 需要添加spring webmvc模块-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
打包方式改为war
<!-- spring mvc2 修改打包方式-->
<packaging>war</packaging>
新建 webapp目标
新建 web.xml
配置web.xml加载 springmvc.xml与applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--设置配置文件的路径 service dao-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--解决中文乱码的过滤器-->
<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>
<!--配置Spring的监听器,默认只加载WEB-INF目录下的applicationContext.xml配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--web.xml 配置前端控制器 DispatcherServlet , 还要加载springmvc.xml-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--加载springmvc.xml
init-param 是DispatcherServlet的初始化参数,指定springmvc的路径及文件名称
当该Servlet 运行,springmvc的配置文件也会被加载进来
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--设置servlet的启动加载时机
load-on-startup 是servlet的加载时机, 1-5值,取值越小,加载时机越早
-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--
配置方式3种:
1.绝对路径 /UserServlet
2.多级目录 /aaa/bbb/UserServlet
3.通配符匹配 *.action *.do (只要请求的后置是.action 或.do都会执行该servlet)
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
配置springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc" 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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--springmvc是web层 UserController @Controller -->
<!-- 打开组件扫描-->
<context:component-scan base-package="com.huawei">
<!-- 只处理带@Controller的请求-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置的视图解析器对象 /WEB-INF/pages/success.jsp -->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--过滤静态资源 .js .css png-->
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/images/" mapping="/images/**" />
<mvc:resources location="/js/" mapping="/js/**" />
<!--开启SpringMVC注解的支持 @RequestMapping @RequestBody @ResponseBody-->
<mvc:annotation-driven/>
</beans>
修改applicationContext.xml中的组件扫描,不扫@Controller注解标记的类
<!-- 扫描指定的目标,只要有@Service,@Repository @Component-->
<context:component-scan base-package="com.huawei">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
SpringMVC编写Controller
- (1)编写Controller类
- (2)配置@Controller注解
- (3)@RequestMapping设置模块
- (4)@RequestMapping设置方法路径与请求方式
//http://localhost:8080/
//当前类要由springMVc创建
@Controller
@RequestMapping(path = "/product")
public class ProductController {
public ProductController() {
System.out.println("ProductController构造方法执行");
}
//方法 = 方法名 + 参数 +返回值
@Autowired
IProductService productService ;
@RequestMapping(path = "/list",method = RequestMethod.GET)
public String list(){
return "list-product";//去/WEB-INF/pages/找名为list-product.jsp的页面
}
Controller方法中调用Service
- (1)Service方法是经过单元测试的所以没有问题
- (2)Controller只做三件事
- (3)1,接收参数
- (4)2,处理参数
- (5)3,响应给浏览器
//http://localhost:8080/
//当前类要由springMVc创建
@Controller
@RequestMapping(path = "/product")
public class ProductController {
public ProductController() {
System.out.println("ProductController构造方法执行");
}
//方法 = 方法名 + 参数 +返回值
@Autowired
IProductService productService ;
@RequestMapping(path = "/list",method = RequestMethod.GET)
public String list(Model model){
System.out.println("list被调用了");
List<Product> list = productService.findAll();
//请求转发 request 也可以使用Model
model.addAttribute("list",list);
return "list-product";//去/WEB-INF/pages/找名为list-product.jsp的页面
}
jsp页面显示编写
- (1)准备好jsp页面
- (2)页面使用el表达式与c标签进行数据显示
list-product.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
<% pageContext.setAttribute("path",request.getContextPath()); %>
</head>
<body>
我是产品列表页面
<a href="${path}/product/toAdd">新建</a>
<table width="100%" border="1">
<tr>
<td>id</td>
<td>商品名</td>
<td>商品价格</td>
<td>操作</td>
</tr>
<c:forEach items="${list}" var="p">
<tr>
<td>${p.id}</td>
<td>${p.name}</td>
<td>${p.price}</td>
<td><a href="${path}/product/delete?id=${p.id}">删除</a>|<a href="${path}/product/toUpdate?id=${p.id}">编辑</a></td>
</tr>
</c:forEach>
</table>
</body>
</html>
删除方法编写
- (1)接收参数,保需定义参数名与页面参数同名即可
- (2)删除成功之后跳到列表页面
@RequestMapping(path = "/delete",method = RequestMethod.GET)
public String delete(String id){
System.out.println("list被调用了");
productService.deleteProducts(id);
return "redirect:/product/list";
}
添加方法编写
- (1)先打开添加页面
- (2)然后再将添加页面的表单数据使用实体对象接收
- (3)调用service添加实体对象
- (4)跳转到列表页面
add-product.jsp
<form action="${path}/product/add" method="post">
<table width="100%" border="1">
<tr>
<td>商品名 <input name="name" type="text"></td>
</tr>
<tr>
<td>商品价格 <input name="price" type="text"></td>
</tr>
<tr>
<td><input value="保存" type="submit"></td>
</tr>
</table>
</form>
@RequestMapping(path = "/toAdd",method = RequestMethod.GET)
public String toAdd(){
return "add-product";
}
@RequestMapping(path = "/add",method = RequestMethod.POST)
public String add(Product product){//使用表单对应的实体类可以接收
productService.saveProduct(product);
return "redirect:/product/list";
}
回显方法编写
- (1)先打开编辑页面,需要作一个回显操作
- (2)先查询要编辑的数据
- (3)再将数据显示在页面的输入框
- (4)查询与显示合在一起叫做回显
<form action="${path}/product/update" method="post">
<table width="100%" border="1">
<tr>
<input name="id" type="hidden" value="${p.id}">
<td>商品名 <input name="name" type="text" value="${p.name}"></td>
</tr>
<tr>
<td>商品价格 <input name="price" type="text" value="${p.price}"></td>
</tr>
<tr>
<td><input value="保存" type="submit"></td>
</tr>
</table>
</form>
@RequestMapping(path = "/toUpdate",method = RequestMethod.GET)
public String toUpdate(String id,Model model){
Product p = productService.findById(id);
model.addAttribute("p",p);
return "update-product";
}
编辑方法编写
- (1)使用Product product 变量接收页面提交的表单数据
- (2)调用service将表单数据保存到数据库中
- (3)保存成功之后跳到列表页面
@RequestMapping(path = "/update",method = RequestMethod.POST)
public String update(Product product){//使用表单对应的实体类可以接收
productService.updateProduct(product);
return "redirect:/product/list";
}