第一章 复习
1. JDBC
JDBC是一套JAVA提供的连接数据库的api(应用编程接口),官方指定的一套标准的接口集,并没有提供具体的实现
需要用户提供驱动,url,用户名和密码。
2.ProPerties工具类
- properties工具类通常用来存放.properties文件,以键值对的形式保存数据
- 一般把系统中的各种参数放在配置文件中,使用这个类进行读取
package com.etoak.testpro;
import java.util.*;
import java.io.*;
//没打包: javac 文件名字 A.java
//package: javac -d . *.java
//set classpath:类路径==》类加载器加载类的时候从类路径中加载类。classpath=.;d:\lib\ojdbc6.jar;d:\dom4j-1.6.1.jar;
public class TestPro{
public static void main(String args[])throws Exception{
//Class.forName("com.mysql.jdbc.Driver");
Properties pro = new Properties();
System.out.println(pro);
//加载资源 从TestPro.class这个class文件所在位置触发寻找 指定文件
//InputStream is = TestPro.class.getResourceAsStream("../../../db.properties");
//com.etoak.entity.Student
InputStream is = TestPro.class.getClassLoader().getResourceAsStream("db.properties");
pro.load(is);
is.close();
System.out.println(pro);
//Object value = Map.get(String key)
String driver = pro.getProperty("o.driver");
System.out.println(driver);
}
}
两种寻找资源的区别
- XXX.class.getResourceAsStream从类的位置开是寻找
- XXX.class.getClassLoader().getResourceAsStream(“db.properties”)从包外边开始找,比较方便
3. 事务
多条语句,一起执行一个执行单元
应用场景
- A账户-100,B账户+100 两条语句最小的单元
3.1 ACID特征
特性 | 解释 |
---|---|
原子性 | 事务不可再分 |
一致性 | 数据类型保持一致 |
持久性 | 事务能将数据持久化到数据库中 |
隔离性 | 多个事务之间会产生一些隔离性问题 |
3.2 隔离性可能产生的问题
-
事务之间没有隔离可能会出现的问题
问题 解释 脏读 事务B读取到了事务A中未提交的数据,然后事务A对刚才的操作进行了回滚操作,那么事务B读取到的数据就无效了 不可重复度 事务A对表内数据提交后,事务B再次读取数据,发现两次读取的数据不一样。 幻读 事务A对表新增数据并提交,事务B再次读取这张表,发现数据多了,就像发生了幻觉一样。 -
为了解决事务之间可能出现的问题,SQL官方提出了四种隔离级别
隔离级别 脏读 不可重复读 幻读 Read uncommitted(未提交读) 会 会 会 Read committed(提交读)[Oracle默认级别] 不会 会 会 Repeatable read(可重复读)[mysql默认] 不会 不会 会 Serializable(序列化读) 不会 不会 不会
3.3 JDBC控制事务
jdbc默认的事务提交模式是自动提交的,当我们写完sql语句并且 execute/executeUpdate 执行完之后,立即提交
//开事务
Connection.setAutoCommit(false);
sql1;
sql2;
//提交
Connection.commit();
//回滚
Connection.rollback();
4.元数据
1. 获取数据库的元数据
存放数据库的基本信息,数据库的版本之类的
import java.sql.*;
import java.io.*;
import javax.swing.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class TestDBMD{
public static void main(String args[])throws Exception{
while(true){
String dbName = JOptionPane.showInputDialog("请选择数据库");
if(null == dbName || "".equals(dbName) || "exit".equals(dbName)) return;
Workbook wb = new XSSFWorkbook();
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql:///"+dbName,"root","dream");
String sql1="show tables";
PreparedStatement pst1 = con.prepareStatement(sql1);
ResultSet rs1 = pst1.executeQuery();
while(rs1.next()){
String sql="select * from "+rs1.getString(1);
Sheet sheet = wb.createSheet(rs1.getString(1));
PreparedStatement pst = con.prepareStatement(sql);
ResultSet rs = pst.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int count = rsmd.getColumnCount();
Row header = sheet.createRow(0);
int j = 1;
while(rs.next()){
Row data = sheet.createRow(j++);
System.out.println(j++);
for(int i=1;i<=count;i++){
//列本身的信息 在ResultSetMetaData中存放
String cName = rsmd.getColumnName(i);
header.createCell(i-1).setCellValue(cName);
String cType = rsmd.getColumnTypeName(i);
//列对应的内容在ResultSet
String cValue = rs.getString(cName);
cValue = cValue == null ? "" : cValue;
data.createCell(i-1).setCellValue(cValue);
}
}
}
OutputStream os = new FileOutputStream(dbName + ".xlsx");
wb.write(os);
wb.close();
os.close();
//获得数据库的元数据
/*DatabaseMetaData dbmd = con.getMetaData();
int v1 = dbmd.getDatabaseMajorVersion();
int v2 = dbmd.getDatabaseMinorVersion();
String driverName = dbmd.getDriverName();
System.out.println(driverName);
System.out.println(v1+"."+v2);*/
}
}
}
5. 使用POI处理excel
核心API Workbook Sheet Row Cell
maven引入依赖
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency>
HSSFWorkbook写出xls文档,最多65535条,XSSFWorkbook写出xlsx文档,30W没问题
1.读取Excel
import org.apache.poi.ss.usermodel.*;//Workbook Sheet Row Cell
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
//import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.io.*;
//读取Excel
public class TestRead{
public static void main(String args[])throws Exception{
//1.构造Workbook对象
Workbook wb = new XSSFWorkbook(new File("etoak.xlsx"));
//2.获得页
Sheet sheet = wb.getSheetAt(0);
//3.获得行范围
int firstRow = sheet.getFirstRowNum();
int lastRow = sheet.getLastRowNum();
for(int i=firstRow;i<=lastRow;i++){
//4.获得某一行
Row row = sheet.getRow(i);
//5.获得列范围
int firstC = row.getFirstCellNum();
int lastC = row.getLastCellNum();
for(int k=firstC;k<lastC;k++){
//6.获得某一列
Cell cell = row.getCell(k);
if(cell.getCellType()==Cell.CELL_TYPE_STRING){
String cvalue = cell.getStringCellValue();
System.out.print(cvalue+"==>");
}else{
double d = cell.getNumericCellValue();
System.out.print(((Double)d).intValue()+"==>");
}
}
System.out.println();
}
}
}
2.创建Excel
注意点
如果有模板文件,则将模板文件传入到XSSFWorkbook的构造方法中,如下:
String path = "D:\study\etoak.xlsx"; /** * 当从服务器获取数据时。需要获取到数据的真实地址(好像就是绝对路径) * ServletContext application = this.getServletContext(); * String path = application.getRealPath("/files/xxx.xlsx"); */ Workbook wb = new XSSFWorkbook(new File(path)); /** *从服务器往客户端输出也有所不同 * wb.write(response.getOutputStream()); */
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
//测试使用POI创建Excel文档
public class TestCreate{
public static void main(String args[])throws Exception{
long time1 = System.currentTimeMillis();
try{
//1.读文件到内存---》Workbook 写 内存中构造Workbook
Workbook wb = new XSSFWorkbook();
//2.给Workbook添加Sheet
Sheet sheet = wb.createSheet("学生列表");
//3.创建行
Row header = sheet.createRow(0);
header.createCell(0).setCellValue("id");
header.createCell(1).setCellValue("name");
header.createCell(2).setCellValue("age");
header.createCell(3).setCellValue("email");
for(int i=1;i<1000000;i++){
Row data = sheet.createRow(i);
data.createCell(0).setCellValue("ET"+i);
data.createCell(1).setCellValue("etoak"+i);
data.createCell(2).setCellValue(10+i);
data.createCell(3).setCellValue("ETOAK"+i+ "@QQ.COM");
}
//4.写出到文件
OutputStream os = new FileOutputStream(new File("stu.xlsx"));
wb.write(os);
wb.close();
}catch(Exception e){
e.printStackTrace();
}
long time2 = System.currentTimeMillis();
System.out.println((time2-time1)/1000);
}
}
6. DBCP 数据库连接池
数据库连接池,需要设置数据库连接需要的四个参数
setDriverClassName() 驱动
setUrl() URL
setUsername() 用户名
setPassword() 密码
设置其他功能
setMaxActive(Integer ) 设置最大活动数
setMaxWait() 毫秒值 最大等待时间 超时异常
setMaxIdle() 设置空闲连接
import org.apache.commons.dbcp.*;
import java.sql.*;
public class TestDBCP{
public static void main(String args[]) throws Exception{
BasicDataSource bds = new BasicDataSource();
bds.setUrl("jdbc:mysql:///et1912");
bds.setDriverClassName("com.mysql.jdbc.Driver");
bds.setUsername("root");
bds.setPassword("dream");
Connection conn = bds.getConnection();
System.out.println(conn);
}
}
使用QueryRunner的有参构造方法
使用有参的构造方法,传进去一个数据源,DbUtils会帮助我们关闭这个链接,而自己创建的则不会关闭
public class Factory{
public static DataSource ds = new DataSource();
//2.属性赋值 driver url user pwd
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///orderfood");
ds.setUsername("root");
ds.setPassword("dream");
return ds;
}
public DaoImpl{
QueryRunner qr;
public List<Test> queryAll(){
String sql = "select * from test";
try{
qr = new QueryRunner(Factory.getDs());
return qr.query(sql,new BeanListHandler<Test>(Test.class));
}catch(Exception e){
e.printStackTrace();
}
}
}
第二章 maven
1. 简介
需要配置maven_home 和path,maven是纯java书写的所以依赖于java_home
配置完成之后cmd mvn -v查看是否成功
更改为阿里云镜像和修改本地仓库地址本地仓库
<!--在maven\conf\setting.xml的第53行和153行左右-->
<localRepository>E:\maven\repository</localRepository>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
maven compile提示groupId等 missing
找到本地仓库中的org\apache\maven\plugins\maven-resources-plugin\对应的版本号的文件夹中查看是否有 .jar文件,如果没有的话,删除文件夹重新编译,可能是网速的问题,如果三五次下载不了就更换为原来的镜像
maven项目需要有以下的文件夹
2.maven常用命令
命令 | 作用 |
---|---|
mvn clean | 删除target |
mvn compile | 编译 |
mvn test | 单元测试 |
mvn package | 打包 |
mvn install | 安装到本地仓库 |
1.范围
范围 | 运行期 | 编译 | 测试 |
---|---|---|---|
compile | 需要 | 需要 | 默认 |
test | 不需要 | 不需要 | 需要 |
provided | 不需要 | 需要 | 不需要 |
3.使用IDEA创建maven项目
1. 创建普通的java项目
- create new project
- 选中maven 使用框架
- 选中 后缀是 quickstart的
创建完成之后修改web.xml为以下信息
不修改xml文件会导致dtd版本太低,无法使用el表达式
<?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_3_1.xsd"
version="3.1">
</web-app>
解决使用骨架创建的web工程web.xml版本过低的问题
-
暂时解决
找到一份高版本的替换现有的xml头部
-
永久解决
找到自己的仓库地址,找到
org/apache/maven/archetypes/maven-archetype-webapp/1.X/maven-archetype-webapp-1.X.jar
使用压缩工具打开
找到 maven-archetype-webapp-1.4.jar\archetype-resources\src\main\webapp\WEB-INF\web.xml
使用记事本打开web.xml
将里边的内容更改为高版本的dtd 保存并且替换原来的web.xml
第三章 文件上传与下载
1. 使用form提交
使用文件上传必须将form更改为
<form enctype="multipart/form-data" method="post"></form>
添加jar包或者依赖
<!--commons-fileupload-1.3.2.jar-->
<!--commons-io-2.2.jar-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
分发方法
String method = request.getParameter("method");
//防止是真的没有填写method这种情况
if(null== method ){
String ct = request.getHeader("Content-Type");
if(ct.indexOf("mult") >= 0){
this.add(request, response);
return;
}
}
上传接口
protected void updateUser(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
HttpSession session = request.getSession();
User u = new User();
try{
List<FileItem> items = upload.parseRequest(request);
Iterator<FileItem> car = items.iterator();
while(car.hasNext()){
FileItem item = car.next();
if(item.isFormField()){
String name = item.getFieldName();
String value = item.getString("utf-8");
//此处填写上传表单的各个属性
if("id".equals(name)){u.setId(Integer.parseInt(value));continue;}
if("name".equals(name)){u.setName(value);continue;}
if("pass".equals(name)){u.setPass(value);continue;}
if("tel".equals(name)){u.setTel(value);continue;}
if("address".equals(name)){u.setAddress(value);continue;}
}else{
String name = item.getName();
if(name.length() > 0) {
ServletContext application = this.getServletContext();
String ext = name.substring(name.lastIndexOf("."));
String newName = UUID.randomUUID().toString().replace("-", "") + ext;
System.out.println("新名字" + newName);
String path = application.getRealPath("upload/" + newName);
item.write(new File(path));
System.out.println("图片路径" + path);
u.setImg(request.getContextPath() + "/upload/" + newName);
}else{
u.setImg(((User)session.getAttribute("u")).getImg());
}
}
}
UserDaoIf dao = new UserDaoImpl();
OutputJson out = new OutputJson(request, response);
System.out.println("更新结果:"+dao.updateUserById(u));
if(dao.updateUserById(u)) {
session.setAttribute("u",u);
out.writeSuc();
return;
}
out.writeErr();
}catch(Exception e){
e.printStackTrace();
}
}
2.js版本的
表单提交时
<script>
form.on('submit(myForm)',function(data){
let datas = new FormData($('#msg')[0]);
$.ajax({
type: 'post',
url: 'commons?method=updateUser',//请求地址
cache: false,
data: datas,
processData: false, //特别注意这个属性不能省
contentType: false, //特别注意这个属性不能省
dataType: 'text',
success(data) {
if (data == 'suc'){
layer.msg('修改成功')
$(location).attr('href',
`${pageContext.request.contextPath}/userinfo.jsp`)
}
}
})
})
</script>
3. ajaxFileUpload
异步提交
//此处使用了vue 的 v-model双向绑定
subform(){
alert(this.brand+'==>' + this.type + '==>' + this.num +'\t' + this.supplier)
$.ajaxFileUpload({
type:'post',
url:'bike',
data:{
"brand":this.brand,
"num":this.num,
"type":this.type,
"supplier":this.supplier
},
/*上传图片的元素ID*/
fileElementId:'pic',
dataType:'json',
success:data=>{
console.log(data)
this.brand=""
this.type=""
this.supplier=""
this.num=""
$('#myModal').modal("hide");
this.queryData(this.pn,this.ps)
}
})
},
4.文件下载
文件下载必须是同步的
<button type="button" onclick="download()">点击下载</button> <script> function download(){ window.location.href=`url?method=XXX` /** * $(location).attr('href',`url?method=XXX`) */ } </script>
给响应设置格式,浏览器不展示以附件的形式下载
response.setHeader("Content-Disposition","attachment;filename=book.xlsx");
下载的实例
protected void export2Excel(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
String path = this.getServletContext().getRealPath("/WEB-INF/template/book.xlsx");
try{
BookService bs = new BookService();
Workbook wb = new XSSFWorkbook(new File(path));
Sheet sheet = wb.getSheetAt(0);
List<Book> data = bs.querySome(new Book(),1,100);
int i = 2;
for(Book d:data){
Row row = sheet.createRow(i++);
row.createCell(0).setCellValue(i-2);
row.createCell(1).setCellValue(d.getName());
row.createCell(2).setCellValue(d.getAuthor());
row.createCell(3).setCellValue(d.getPrice());
row.createCell(4).setCellValue(d.getPub().getName());
row.createCell(5).setCellValue(d.getPub().getPhone());
row.createCell(6).setCellValue(d.getPub().getLoc());
row.createCell(7).setCellValue(d.getCa().getName());
row.createCell(8).setCellValue(d.getPdate());
row.createCell(9).setCellValue(d.getStock());
row.createCell(10).setCellValue(d.getStatus());
row.createCell(11).setCellValue(d.getDel());
}
response.setHeader("Content-Disposition","attachment;filename=book.xlsx");
wb.write(response.getOutputStream());
wb.close();
}catch(Exception e){
e.printStackTrace();
}
}
5.多文件上传
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
// Parse the request
List<FileItem> items = upload.parseRequest(req);
Iterator<FileItem> iter = items.iterator();
Book book =new Book();
List<Bookpic> bps = new ArrayList<>();
while (iter.hasNext()) {
//<input type="text" name="name"
//<input type="text" name="author"
//<input type="text" name="publishdate"
//<select name="publisherid"
//<input type=file" name="pic">
FileItem item = iter.next();
if(item.isFormField()){
//普通表单参数
String name = item.getFieldName();
String value = item.getString("utf-8");
if("name".equals(name)){book.setName(value); }
if("author".equals(name)){book.setAuthor(value); }
if("price".equals(name)){book.setPrice(Double.parseDouble(value)); }
if("pubdate".equals(name)){book.setPublishdate(ETDateUtils.string2Date(value)); }
if("pub".equals(name)){book.setPublisherid(value); }
if("ca".equals(name)){book.setCategoryid(value); }
if("stock".equals(name)){book.setStock(Integer.parseInt(value)); }
if("status".equals(name)){book.setStatus(Integer.parseInt(value)); }
}else{
//文件上传控件 asdfadfafd.jpg 10m 2234234adf.jpg 20m
String fName = item.getName();
//后缀 jpg png gif abc.jpg ==>jpg 123.gif gif
String fExt = FilenameUtils.getExtension(fName);
//改名
String newName = UUID.randomUUID().toString().replaceAll("-","")
+"."+fExt;
//保存到指定位置 servlet->HttpServlet-->GenericServlet-->servlet,SerlvetConfig
ServletContext application = this.getServletContext();
//获得服务器上的绝对路径
String path = application.getRealPath("/files/"+newName);
//写到服务器上
item.write(new File(path));
//把图片地址保存到bp对象中
Bookpic bp = new Bookpic();
bp.setSavepath("/files/"+newName);
bp.setCover(0);
bp.setRealname(fName);
bp.setUploadtime(new java.util.Date());
// bp.setBookid();==>service-->book-->
bps.add(bp);
}
}
您的主机意外终止了一个链接
button 没有设置type=‘button’ 导致提交了好多次
button默认的类型是submit,导致表单提交,需要将类型更改为button
第四 分页
1. 分页数据封装到一个类中
将分页所需的几个元素的封装进一个类中,其中当前页,每页记录数,总记录数,和查询到的记录数是通过set方法传递进来的,其他的参数都是算出来的,查询结束之后吧page这个类的对象返回给前端,
package com.etoak.bicycle.commons.page;
import java.util.List;
//这个类存放与分页相关的数据
public class Page<T> {
private int pageNumber; //当前页
private int pageSize; //每页记录数
private int total; //总记录数
private int pageCount; //页数
private List<T> rows; //这一页的数据
private int pre; //上一页
private int next; //下一页
private int start; //每页起始位置
private boolean first; //第一页
private boolean last; //最后一页
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public List<T> getRows() {
return rows;
}
public void setRows(List<T> rows) {
this.rows = rows;
}
public int getPageCount() {
return (total+pageSize-1)/pageSize;
}
public int getPre() {
if(pageNumber>1)
return pageNumber-1;
return 1;
}
public int getNext() {
if(pageNumber<getPageCount())
return pageNumber+1;
return getPageCount();
}
public int getStart() {
return (pageNumber-1)*pageSize;
}
public boolean isFirst() {
return pageNumber==1;
}
public boolean isLast() {
return pageNumber==getPageCount();
}
}
protected void querySome(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("application/json;charset=utf-8");
Page<Bicycle> page = new Page<>();
page.setPageNumber(Integer.parseInt(request.getParameter("pageNumber")));
page.setPageSize(Integer.parseInt(request.getParameter("pageSize")));
BicycleService bs = new BicycleService();
int total = bs.total(new Bicycle());
page.setTotal(total);
List<Bicycle> list = bs.querySome(new Bicycle(),page.getStart(),page.getPageSize());
page.setRows(list);
PrintWriter out = response.getWriter();
out.print(JSONObject.toJSONString(page));
out.close();
}
前端按钮和js
<button type="button" class="btn btn-primary" @click="queryData(1,ps)">首页</button>
<button type="button" class="btn btn-success" @click="queryData(pre,ps)">上一页</button>
<button type="button" class="btn btn-warning" @click="queryData(next,ps)">下一页</button>
<button type="button" class="btn btn-danger" @click="queryData(end,ps)">尾页</button>
data:{
bikes:[],
pn:1, //当前页 pageNumber
ps:3, //每页记录数
pre:0,next:0,end:0, start:0,
checked:false,
ids:[],
brand:'',
type:'',
num:'',
supplier:''
},
queryData(pageNumber,pageSize){
//构造key=value形式的参数
const params = new URLSearchParams();
params.append('pageNumber', pageNumber || 1);
params.append('pageSize', pageSize || 3);
params.append('method',"querySome");
console.log(this.pre)
console.log(this.next)
console.log(this.end)
//发送请求。。
axios.post('/bike', params)
.then(response=> {
this.bikes = response.data.rows;
this.pn = response.data.pageNumber;
this.ps = response.data.pageSize;
this.pre = response.data.pre;
this.next = response.data.next;
this.end = response.data.pageCount;
this.start = response.data.start;
})
.catch(function (error) {
console.log(error);
});
},
2. bootstrap分页写法
<table id="tb"></table>
<script>
$(function(){
queryData()
})
queryData(){
$('#tb').bootstrapTable({
url:'book',
pagination:true,
pageNumber:1,
pageSize:5,
pageList:[3,5,6,10],
sidePagination: 'server',
queryParamsType: 'undefined',
queryParams(p){
p.method = 'querySome'
let name = $('#name').val()
let price = $('#price').val()
let ca = $('#ca').val()
let pub = $('#pub').val()
if(name != null){
p.name = name
}
if(price != null){
p.price = price
}
if(ca != null){
p.ca = ca
}
if(pub != null){
p.pub = pub
}
start = (this.pageNumber - 1) * this.pageSize
return p
},
columns:[
{checkbox:true},
{title:'序号',field:'id',formatter(value,row,index){
return start + index + 1
}},
{title:'名称',field:'name'},
{title:'作者',field:'author'},
{title:'单价',field:'price'},
{title:'出版日期',field:'pdate'},
{title:'出版社',field:'pub.name'},
{title:'出版社电话',field:'pub.phone'},
{title:'出版社地址',field: 'pub.loc'},
{title:'所在类别',field: 'ca.name'},
{title:'状态',field: 'status',formatter(value,row,index){
return value == 1 ?'上架' : '下架'
}},
{title:'是否删除',field: 'del',formatter(value,row,index){
return value == 1 ? '已删除' : '未删除'
}},
{
title:'操作',
formatter(value,row,index){
return "<a href='javascript:;' id='info'>显示</a>"
},
events:etevent
}
]
})
}
window.etevent = {
//todo
}
</script>
第五章 mybatis
1. JDBC与ORM
1.JDBC的不足
- 代码冗余,重复代码特别多
- 面向对象的开发《==》面向关系的数据库不对应。需要我们手动实现对应关系。
2. 引入ORM
ORM:Object Relational Mapping 对象关系映射。主要目的就是把面向对象开发中的对象,映射到基于SQL的关系型数据库中。
- Hibernate: 全自动的ORM实现。 自动生成SQL语句,自动的执行SQL语句、自动返回结果。
① 痛点:效率低、 SQL不一定非常符合复杂逻辑。
② 学习成本高。
(2) MyBatis: 半自动的ORM实现 SQL语句需要自己写的、自动执行SQL、自动返回结果 SQLMapper 框架
2.使用MyBatis进行curd操作
1. 引入maven依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.添加log4j和config配置文件
- Log4j
### set log levels ###
log4j.rootLogger = debug , stdout , D , E
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} ===== %5p %c{ 1 }:%L - %m%n
#### 输出到日志文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#
#### 保存异常信息到单独文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/error.log ## 异常日志文件名
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
- config.xml
注册别名 当给包起别名的时候,该包及其子包下的类默认类名小写
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<!--给包起别名 com.etoak.book包及其子包的下的类 别名默认类名字首字母小写 -->
<package name="com.etoak.book"/>
</typeAliases>
<!--配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
<!--从db.properties获取-->
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${m.driver}"/>
<property name="url" value="${m.url}"/>
<property name="username" value="${m.user}"/>
<property name="password" value="${m.pwd}"/>
</dataSource>
</environment>
</environments>
<!--注册mapper-->
<mappers>
<mapper resource="xml/BookMapper.xml"></mapper>
<mapper resource="xml/BookpicMapper.xml"></mapper>
<mapper resource="xml/CategoryMapper.xml"></mapper>
<mapper resource="xml/LocationsMapper.xml"></mapper>
<mapper resource="xml/PublisherMapper.xml"></mapper>
</mappers>
</configuration>
3. 创建工厂
package com.etoak.book.commons.factory;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.Reader;
public class SF {
private static SqlSessionFactory f;
private SF(){}
static{
try{
Reader reader = Resources.
getResourceAsReader("config.xml");
f = new SqlSessionFactoryBuilder().build(reader);
}catch(Exception e){
e.printStackTrace();
}
}
public static SqlSession getSession(){
return f.openSession();
}
}
执行完成之后一定要commit()否则会回滚
3.动态标签
- where
<select id="queryByNameOrAge" resultMap="rMap_stu">
select * from tb_stu
<where>
<if test="name != null and name != ''">
and s_name like '%${name}%'
</if>
<if test="age != null">
and s_age =#{age}
</if>
</where>
</select>
- foreach
<delete id="delBat">
delete form user where id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</delete>
where和if的区别
4. 错误集锦
- 没有返回类型
A query was run and no Result Maps were found for the Mapped Statement ‘mapper.StudentMapper.queryById’. It’s likely that neither a Result Type nor a Result Map was specified.
引发原因:sql的映射文件中没有设置返回类型,
解决办法:在xml文件对应的查询语句后边加上合适的返回类型 resultType
- sql语句执行了紧接着回滚
引发原因:sql语句执行之后没有提交
解决办法:在直接SQL语句之后添加 session.commit()
- log4j error
有可能是log4j.properties没放在resource文件夹下
- the type interface in not know in MapperRegiety
xml文件中的namespace写错了,或者config.xml没有加上要注册的sql映射文件
- 同名ID
Error parsing Mapper XML. The XML location is ‘xml/StudentMapper.xml’. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for mapper.StudentMapper.XXX
StudentMapper.xml有两个id为XXX的方法
- 模糊查询错误
There is no getter for property named ‘name’ in ‘class java.lang.String’
There is no getter for property named 'name’ in ‘class java.lang.String’
模糊查询时 使用的 $ ,以本错误窝里需要加上注解name ,需要在XXXMapper中的参数前边加上注解 @Param(“name”);
public List<Student> queryByLike(@Param("name") String name);
- sql语句用错符号
Error evaluating expression ‘name !+ null’. Cause: org.apache.ibatis.ognl.ExpressionSyntaxException: Malformed OGNL expression: name !+ null [org.apache.ibatis.ognl.ParseException: Encountered " “!” "! “” at line 1, column 6.
sql语句中符号用错了
- 参数没发现,绑定错误
org.apache.ibatis.binding.BindingException Parameter XXX not found 属性绑定错误
引发原因:未知
解决办法:未知 添加@Param(XXX)
- 数据被截断了
java.sql.SQLException: Data truncated for column ‘publisherid’ at row 1
引发原因:传入的数据库太长了
更改数据库字段长度
- 1 不是一个十进制数字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LjvMsXGL-1602465857574)(C:\Users\eleven\AppData\Roaming\Typora\typora-user-images\image-20200527161131599.png)]
11.添加时出错,主键没有一个默认值
java.sql.SQLException: Field ‘id’ doesn’t have a default value
引发原因:使用代码生成器创建mapper.xml时,没有给id默认值
<generatedKey column="deptno" sqlStatement="select replace(uuid(),'-','') as id" identity="false" />
5.代码生成器
- 引入依赖
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
- 加入插件
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</plugin>
- 在 resources中添加generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<classPathEntry location="D:/lib/driver/ojdbc6.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--此处填写jdbc的驱动-->
<jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@localhost:1521:orcl"
userId="scott"
password="tiger">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--实体类生成的包路径-->
<javaModelGenerator targetPackage="com.et1812.emp.entity"
targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="mappers"
targetProject="src/main/resources">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!--Mapper生成的路径-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.et1812.emp.dao"
targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table schema="scott" tableName="emp"
domainObjectName="Emp" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="empno" sqlStatement="select max(empno)+1 as empno from emp" identity="false" />
</table>
<table schema="scott" tableName="dept"
domainObjectName="Dept" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="deptno" sqlStatement="select max(deptno)+1 from emp" identity="false" />
</table>
<table schema="scott" tableName="salgrade"
domainObjectName="Salgrade" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="grade" sqlStatement="select max(grade)+1 from emp" identity="false" />
</table>
</context>
</generatorConfiguration>
需要修改的地方为
<!--修改为自己的jar包路径-->
<classPathEntry location="D:/lib/driver/ojdbc6.jar" />
<!--这个标签内修改为自己的路径-->
<jdbcConnection></jdbcConnection>
<!--修改为自己的实体类生成路径-->
<!--下边三个都是修改targetPackage的后边的内容-->
<javaModelGenerator></javaModelGenerator>
<!--修改为自己的Mapper路径-->
<javaClientGenerator></javaClientGenerator>
<!--修改为自己的Mapper.xml路径-->
<sqlMapGenerator></sqlMapGenerator>
<!--要生成的数据表-->
<!--schema 为数据库源 -->
<table schema="scott" tableName="dept"
domainObjectName="Dept" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="deptno" sqlStatement="select max(deptno)+1 from emp" identity="false" />
<!--mysql生成自动增长得字符串主键-->
<!--
<generatedKey column="deptno" sqlStatement="select replace(uuid(),'-','') as id" identity="false" />
-->
- 点击edit configtaion
点击➕,选中maven 改名字为maven_generator(改不改都行,与一个名字而已),
Working Directory选中自己的model,在命令行中插入
mybatis-generator:generate -e
6.分页
- 引入pageHelper插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
- 在config.xml添加插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- config params as the following -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
7.连表查询
- sql 语句的 id 对应的是 xxxMapper 的方法名
- 当数据库中的字段名和实体类的属性名对应的时候使用resultType即可,当不对应的时候或者是连表查询的时候使用 resultMap ,使用resultMap是后边填写的要和resultMap 标签的id相对应
- 返回是 id 一般是主键这种唯一的
- property 对应的是实体类中的属性, column对应的是查询出来的列名
- 给实体类中其他类的对象赋值时使用association标签,给实体类中的集合赋值时使用collection标签
测试所需的实体类
public class Book{
private Integer id;
private String name;
private Double price;
private Author au;
private List<Pic> pics;
//getter and setter
}
public class Author{
private String id;
private Integer bookid;
private String name;
//getter and setter
}
public class Pic{
private String id;
private Integer bookid;
private String url;
}
- 给查询的实体里边的属性赋值
association 此标签是给返回类型里边的其他类的对象赋值的,property是其他类对象的名字,此处对应上边的 au javaType指的是au这个对象的数据类型
<resultMap id="suibianxie" type="Book">
<id property="id" column="bid"></id>
<result property="name" column="bname"></result>
<result property="price" column="price"></result>
<association property="au" javaType="Author">
<id property="id" column="aid"></id>
<result property="bookid" column="abid"></result>
<result property="name" column="aname"></result>
</association>
</resultMap>
<select id="queryBook" paramType="Book" resultMap="suibianxie">
select b.id as bid,b.name as bname,price,a.id as aid,a.bookid as abid,a.name as anme,p.id as pid,p.bookid as pbid,url
from book b left join author a on b.id = a.bookid
left join pic p on b.id=pic.bookid
</select>
- 给集合赋值
collection 给集合赋值,在返回的 Book 对象中有一个集合叫 pics ,ofType里边填写的就是这个pics的泛型
<resultMap id="suibianxie" type="Book">
<id property="id" column="bid"></id>
<result property="name" column="bname"></result>
<result property="price" column="price"></result>
<collection property="pics" ofType="Pic">
<id property="id" column="pid"></id>
<result property="bookid" column="pbid"></result>
<result property="url" column="url"></result>
</collection>
</resultMap>
#和$的区别
#:会将传入的解析成一个字符串,能够防止sql注入
$:将传入的值直接解析在sql语句中,不能够防止SQL注入
8.批量操作
<insert id="insertBat">
insert into pic(id,name,url) values
<foreach collection="list" item="p" open="(" close=")" separator=",">
(replace(uuid(),'-',''),#{p.name},#{p.url})
</foreach>
</insert>
第六章 bootstrap总结
1.分页
bootstrap需要的数据有total和rows,所以从后台返回数据的时候必须要有上边两个的数据
List<Test> data = dao.query(); PageInfo<Test> info = new PageInfo<>(data); Map<String,Object> map = new HashMap<>(); map.put("total",info.getTotal()); map.put("rows",info.getList()); //输出为json格式
layui所需要的为code,msg,count,data
Integer allRecord = dao.getCount(); List<Test> data = dao.queryData(); Map<String,Object> map = new HashMap<>(); map.put("code",""); map.put("msg",""); map.put("count",allRecord); map.put("data",data); //输出为json格式
<table id="tb"></table>
<script>
$(function(){
$('#tb').bootstrapTable({
url:'bike',
contentType:'application/x-www-from-urlencoded',
pagination:true, //是否分页,默认不分页
pageNumber:1,
pageSize:3,
pageList:[3,4,5,6,,10],
sidePagination: 'server' , //服务器端分页
queryParamsType: 'undefined',
queryParams(params){
params.method = 'querySomeRent'
start = (params.pageNumber - 1) * params.pageSize
return params
},
columns:[
{checkbox:true},
{title:'序号',align:'center',formatter:function (value,row,index) {
return start+index+1
}},
{title:'租赁号',field:'id'},
{title:'租赁人',field:'renterName',align:'center'},
{title:'电话',field:'renterPhone'},
{title:'开始时间',field:'sDate'},
{title:'状态',field:'status',formatter(value,row,index){
return value === '0' ? '租赁中' : '已归还'
}},
{title:'操作',formatter(value,row,index){
let info = "<a id='info' href='javascript:;'>更新</a>"
return info
},events:etevents}
]
})
})
})
</script>
2.树形组件
需要在属性组件的地方加上一个List用在存储子菜单,同时数据库中加入一个父id 的字段来帮助寻找父级菜单,返会的内容里需要有nodes 是组件需要的属性名称
以权限管理为例
- 前端页面
<!DOCTYPE>
<html>
<head>
<title></title>
<link rel="stylesheet" href="css/bootstrap-treeview.min.css">
</head>
<body>
<div id="menus"></div>
<!--引入js和bootstrap树形组件-->
<script>
$(function(){
queryMenus()
})
funciton queryMenus(){
$.post('users',`method=queryMenu`,msg=>{
$('#menus').treeview({
nodeIcon:'glyphicon glyphicon-home',
color:'#FFFFFF',
backColor:'#293846',
onhoverColor:'#orange',
borderColor:'red',
showBorder:false,
showTags:false,
highlightSelected:true,
selectedColor:'yellow',
selectedBackColor:'darkorange',
data:msg,
enableLinks:true
})
},'json')
}
</script>
</body>
</html>
servlet
private void queryMenu(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
Object obj = request.getSession().getAttribute("user");
Users u = null;
if(obj == null){
u = new Users();
u.setId(1);
}else{
u = (Users)obj;
}
UsersService usersService = new UsersService();
List<Roles> roles = usersService.queryRolesByUsersId(u.getId());
Set<Menu> menus = new HashSet<Menu>();
roles.forEach(role -> {
menus.addAll(usersService.queryMenuByRolesId(role.getId()));
});
List<Menu> result = new ArrayList<Menu>();
Iterator<Menu> car = menus.iterator();
while(car.hasNext()){
Menu m = car.next();
if(m.getPid() == -1){
result.add(m);
car.remove();
}
}
result.forEach(x -> usersService.setChildren(x,menus));
ETJSONUtils.writeArray(response,result);
}
实体类
//需要在实体类中添加一个List来存储子菜单
public class Menu(){
private List<Menu> nodes;
public List<Menu> getNodes() {
return nodes;
}
public void setNodes(List<Menu> nodes) {
this.nodes = nodes;
}
}
第七 发短信
1. 发送验证码
- 先获取到前端传过来的手机号
- 用HttpPost访问远程的接口
- 使用BASE64加密自己的开发者ID和那个秘钥
- 给请求头添加相应的内容,按照接口的要求给消息体添加需要的json内容
- 如果发送成功则会返回一个msg_id
protected void sendMsg(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String phone = req.getParameter("phone");
//JDK中URLConnection 可以用来请求别的API
//URLConnection uc = new URLConnection();
//uc.getInputStream()
//HttpClient
HttpPost hp= new HttpPost("https://api.sms.jpush.cn/v1/codes");
BASE64Encoder be = new BASE64Encoder();
String str="83e864bdf90b1bff95685ffb:3fda121d95760cc5539ac620";
String resultStr= be.encode(str.getBytes());
String value = "Basic "+resultStr;
Header header = new BasicHeader("Authorization",value);
//请求头
hp.addHeader(header);
StringEntity entity = new StringEntity("{\"mobile\":\""+phone+"\",\"sign_id\":7044,\"temp_id\":1}");
//消息体
hp.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(hp);
//{"msg_id":"xxxxx"}
String str11 = EntityUtils.toString(response.getEntity(),"UTF-8");
//判断
System.out.println(str11);
JSONObject jo = JSONObject.parseObject(str11);
String msgid = jo.get("msg_id")+"";
req.getSession().setAttribute("msgid",msgid);
//1.传递到客户端
ETJSONUtils.writeObject(resp,msgid);
}
2判断是否登录成功
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//需要获得验证码
String code = req.getParameter("code");
String msgid = req.getParameter("msgid");
//验证验证码是否正确
HttpPost hp= new HttpPost("https://api.sms.jpush.cn/v1/codes/"+msgid+"/valid");
BASE64Encoder be = new BASE64Encoder();
String str="appkey:appsecrete";
String resultStr= be.encode(str.getBytes());
String value = "Basic "+resultStr;
Header header = new BasicHeader("Authorization",value);
//请求头
hp.addHeader(header);
StringEntity entity = new StringEntity("{\"code\":\""+code+"\"}");
//消息体
hp.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = httpClient.execute(hp);
String str11 = EntityUtils.toString(response.getEntity(),"UTF-8");
System.out.println(str11);
JSONObject jo1 = JSONObject.parseObject(str11);
boolean flag = ((boolean)jo1.get("is_valid"));
Map<String,Object> map= new HashMap<String,Object>();
if(!flag){
map.put("flag","验证码错误!");
ETJSONUtils.writeObject(resp,map);return;
}
String name= req.getParameter("name");
String pwd= req.getParameter("pwd");
UserService userService = new UserService();
Users u = userService.queryUserByNameAndPwd(name,pwd);
if(u!=null && ETStringUtils.isNotEmpty(u.getName())){
//登陆成功!
map.put("flag","success");
req.getSession().setAttribute("user",u);
}else{
map.put("flag","fail");
}
ETJSONUtils.writeObject(resp,map);
}
第八章 反射[reflection]
- 反射就是java语言提供的一套在运行期动态获取类中信息的api
- 通过反射我们可以在运行期间动态的创建对象、动态的执行类中的方法、动态获取类中属性、方法、构造方法、动态的判断对象的类型
1.反射实例
//通过反射获取类所有的属性和方法
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
public class ClassUtils {
public static List<String> getMethodsName(Class cls) throws Exception{
Method[] methods = cls.getMethods();
List<String> data = new ArrayList<>();
for(Method m : methods){
String modifier = Modifier.toString(m.getModifiers());
String methodName = m.getName();
String str = modifier + " " + methodName;
data.add(str);
}
return data;
}
public static List<String> getFieldsName(Class cls) throws Exception{
Field[] fields = cls.getFields();
List<String> data = new ArrayList<>();
for(Field f: fields){
String modifier = Modifier.toString(f.getModifiers());
String fType = f.getType().toString();
String fName = f.getName();
String str = modifier + " " +fType + " " + fName;
data.add(str);
}
return data;
}
}
2.反射的应用场景
- 加载数据库驱动的时候。
- 创建servlet对象的时候。
3.反射的核心API
- java.lang.Class
- java.lang.reflect.Method
- java,lang.reflect.Field
第九章 枚举和注解
1. 枚举类的使用
-
当一个类的对象只有有限个,是确定的,我们称此类为枚举类
-
当需要定义一组常量时,建议使用枚举类
-
如果枚举类只有一个对象,则可以作为单例模式的实现方式
1.1 如何定义枚举类
-
JDK5.0以前,自定义枚举类
class Season{ //声明Season对象的属性:private final修饰 private final String name; private final String desc; //2.私有化构造方法,并给对象赋值 private Season(String name,String desc){ this.name = name; this.desc = desc; } //3.提供当前枚举类的多个对象,声明为public static final public static final Season SPRING = new Season("春天","春暖花开"); public static final Season SUMMER = new Season("夏天","夏日炎炎"); public static final Season AUTUMN = new Season("秋天","秋高气爽"); public static final Season WINTER = new Season("冬天","冰天雪地"); public String getName(){ return name; } public String getDesc(){ return desc; } } class Test{ @Test public void test(){ Season spring = Season.SPRING; System.out.println(spring); } }
-
JDK5.0.可以使用enum关键字定义枚举类
- 默认父类是 java.lang.Enum ,不用重写toString() 方法
public class Test{ @Test public void test(){ System.out.println(Season1.SUMMER); } } enum Season1{ //1.提供枚举类的多个对象,对象之间用 , 隔开 最后一个用 ; SPRING("春天","春暖花开"), SUMMER("夏天","夏日炎炎"), AUTUMN("秋天","秋高气爽"), WINTER("冬天","冰天雪地"); private final String name; private final String desc; public String getName(){ return name; } public String getDesc(){ return desc; } }
1.2 Enum的常用方法
-
values()
Season1[] v = Season1.values(); for(Season1 s: v){ System.out.println(s); }
-
valueOf()
//valueOf(String objName) 返回美剧类中对象名是objName的对象 //如果没有对应的objName的枚举类对象,则抛出异常 System.out.println(Season1.valueOf("WINTER"));
1.3 使用 enum 关键字的枚举类实现接口
-
跟普通类一样的实现方法
interface info{ void show(); } enum t implements info{ w,A; @Override public void show(){} }
-
每个对象都重写方法
interface info{ void show(); } enum t implements info{ W{ @Override public void show(){} }, A{ @Override public void show(){} }; }
2. 注解的使用
- 从JDK5.0开始,java增加了对元数据的支持,也就是注解(Annotation)
- 注解就是代码中的**特殊标记**,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理,通过使用注解,可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。
- 注解可以像修饰符一样被使用,可以用来**修饰包,类,构造方法,方法,成员变量,参数,局部参数的声明**,这些信息被保存在注解的“name = value” 对中。
- 可以说 框架 = 注解 + 反射 + 设计模式。
-
生成文档相关的注解
注解 作用 @author 标明开发该类的作者,多个作者用,分开 @version 标明该类模块的版本 @see 参考 @since 从哪个版本开始 @param 对方法中某个参数的说明,没有参数不能写 @return 对返回值的说明,void 不能写 @exception 对可能抛出的异常说明,没有显式的用throws抛出异常的不能写 其中@return @param @exception 只能用于方法
@param 格式要求 @param 形参名 形参类型 形参说明
@return 格式要求 @return 返回值类型 返回值说明
@execption 格式要求 @execption 异常类型 异常说明
@param @execption 可以并列多个
-
在编译时进行格式检查(JDK内置的三个基本注解)
注解 含义 @Override 限定重写父类方法,只能用于方法 @Deprecated 表示修饰的元素已经过时了 @SuppressWarnings 抑制编译器警告 -
如何自定义注解
//声明为@interface public @interface AnnotationTest{ String[] value(); } //自定义注解通常会指明两个元注解,一折Retention指明生命周期,一个Target指明能够声明哪些结构
-
JDK中的四种元注解
Retention、Target、Documentd、Inherited
是对现有注解进行解释说明的注解
- @Retention:指明注解的生命周期,SOURCE \ CLASS (默认行为) \ RUNTIME ,只有声明为RUNTIME生命周期的注解,才能通过反射获取
- @Target: 指明注解可以声明可以哪些结构
- @Documented:表示所修饰的注解在javadoc解析时保留下来
- @Inherited 被修饰的注解有继承性
第十章 Spring
1. spring简介
1.Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE *full-stack(一站式)* 轻量级开源框架。
2.SSH(Spring Struts Hibernate)—>(SSM)–>Spring MVC+MyBatis---->Spring Boot+mybatis+==》微服务Spring cloud
spring的优势
- spring可以降低代码之间的耦合度,通过配置文件来配置代码里边接口的实现类,后期更换实现方法的话也不需要重新编译源代码。
- spring不需要依赖其他的模块或者服务器
- spring全家桶包含或者整合了其他各层的技术,不需要额外的学习其他的内容
- 下载地址:http://repo.spring.io/release/org/springframework
1.1 为什么要解耦
降低代码之间的耦合度,使得开发者只需要关注自己的功能即可,不需要关注其他层面的内容
spring 的头文件
<?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-4.0.xsd">
</beans>
引入mvn依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
2. IOC[xml版本]
- 控制:谁来创建对象的控制权。
- 反转:由我们创建到容易自动创建
IOC的优势
由原来的的主动创建对象,到现在的IOC容器被动注入对象,需要的时候,只需要从容器内拿对象,不用关心对像创建的细节不在负责依赖冠以的处理。全部都交给IOC容器来统一管理,上层服务使用者与下层服务提供这打到了松散耦合的效果,上层只需要关系本层的业务逻辑即可
依赖注入和IOC的关系
依赖注入 DI[Dependency Injection],是指A想要完成一件事情需要B对象的协助,由IOC容器将B注入给A对象。
- IOC控制反转,创建对象
- DI:依赖注入,给对象中需要的属性赋值
- DI和IOC组合在一起,是不能分开的,DI的依赖的IOC
导入外部的数据库配置文件
<!--在spring头文件的beans里边添加-->
<context:property-placeholder location="db.properties"></context:property-placeholder>
1. 构造对象的方法
工厂类先从配置文件中找到id,根据这个id找到对应的实体类,根据实体类通过反射创建对象 class.newInstance()
-
调用无参的构造方法
<bean id="stu" class="com.etoak.entity.Student"></bean>
-
调用静态工厂方法
<!--class是工厂所在类 factory-method为类中的静态方法--> <bean id="stu1" class="com.etoak.factory.StudentFactory" factory-method="createStu"></bean>
package com.etoak.entity; public class StudentFactory { StudentFactory(){ System.out.println("StudentFactory构造。。。。"); } public static Student createStu(){ Student stu = new Student(); return stu; } }
-
调用实例工厂创建对象
<!----> <bean id="stuFactory" class="com.etoak.entity.StudentFactory2" ></bean> <bean id="stu2" factory-bean="stuFactory" factory-method="createStu"></bean>
package com.etoak.entity; public class StudentFactory2 { StudentFactory2(){ System.out.println("StudentFactory2构造。。。。"); } public Student createStu(){ Student stu = new Student(); return stu; } }
-
通过FactoryBean
一般用于和第三方插件整合
<bean id="stu3" class="com.etoak.entity.StudentFactoryBean"></bean>
package com.etoak.entity; import org.springframework.beans.factory.FactoryBean; public class StudentFactoryBean implements FactoryBean { @Override public Student getObject() throws Exception { return new Student(); } @Override public Class<?> getObjectType() { return Student.class; } }
-
通过注解
<!--使用注解版的需要在头部约束文件加上以下文字--> <!-- xmlns:context="http://www.springframework.org/schema/context" xsi-schemaLocation后边加上 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" --> <context:component-san bean-package="com"></context:component-san>
package com.etoak.entity; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; //Student表示我们将要让Spring的IOC容器创建的对象。 //Dao Servevice @Component("stu") public class Student { public Student(String username,String pwd){ this.username = username; this.pwd = pwd; } public void destroy(){ System.out.println("destory...销毁"); } public void init(){ System.out.println("init...."); } public Student(){ System.out.println("Student被构造。。。。"); } @Value("abc") private String username; @Value("123") private String pwd; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
测试
package com.etoak.test;
public class Test {
public static void main(String[] args) throws Exception {
/* BeanFactory f = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
System.out.println(111);
Student s = f.getBean("stu", Student.class);
System.out.println(s);*/
ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(222);
Student stu = factory.getBean("stu",Student.class);
System.out.println(stu);
System.out.println(stu.getName() + "\t" + stu.getPwd());
}
}
BeanFactory和ApplicationContext的区别
- BeanFactory接来自beans包,是SpringIOC容器最基本的接口,负责配置、管理、创建bean。是Spring比较原始的接口
- BeanFactory是延时加载对象的,只有执行getBean的时候才会构造对象。ApplicationContext继承自BeanFactory包,来自context包,是立即加载的,加载配置文件的时候就创建bean对象
- 在实际开发中,spring一般与WEB容器在一起的时候Application对WEB的支持更好
2.IOC容器给对象赋值的方法
1.通过Setter方法
<!--实现过程是先获取到property里边的name属性值,然后拼接成setter方法,最后执行setter赋值-->
<bean id=stu1 init-method="init">
<property name="username" value="book"></property>
<property name="pwd" value="book"></property>
</bean>
- 通过构造方法
<bean id="stu" class="com.etoak.entity.Student">
<constructor-arg type="java.lang.String" value="123"></constructor-arg>
</bean>
- 通过注解
to be continued..
3.bean 的常用属性
属性名 | 作用 |
---|---|
id | 表示 |
name | 与id一致,可以包含特殊字符 |
class | 类型 |
factory-method | 获得对象的方法,静态方法工厂和实例工厂 |
factory-bean | 实例工厂 |
lazy-init | 是否延迟加载,默认false |
init-method | 初始化方法,构造方法完毕,紧接着调用init初始化 |
destory-method | 销毁方法 |
scope | 作用范围 |
bean元素常用的作用范围
序号 | 范围 | 解释[345需要配合web容器使用] |
---|---|---|
1 | singleton | 默认IOC容器只创建一个bean对象 单例 |
2 | prototype | 每次getBean()都会创建一个对象 多实例 |
3 | request | 每次请求都会创建一个对象 |
4 | session | 每个会话创建一个对象 |
5 | application | 整个应用范围内一个对象 |
4.Spring中bean的生命周期(面试用!!!)背过![斜体为选用的]
-
Bean 创建
<!--使用构造方法--> <bean id="" class="" />
-
Setter方法
<!--使用setter方法赋值--> <property name=""></property>
-
各种Aware设置(依次 :BeanNameAware \BeanFactoryAware\ApplicationContextAware)
各种Aware的设置
-
BeanNameAware
-
BeanFactoryAware
-
ApplicationContextAware
-
-
BeanPostProcessor中的 before方法执行。。
-
InitializingBean中的afterPropertiesSet
-
init指定初始化的方法
-
BeanPostProcessor中的after方法执行
-
获得bean 使用bean 【我们】
-
DisposableBean中的destroy方法执行
-
destroy 指定销毁方法
- 如果设置了BeanPostProcessor【后置处理器】的话,那么在init方法执行前后,还会执行BeanPostProcessor中的方法。
- 如果实现了InitializingBean接口,在BeanPostProcessor接口的before方法执行之后,自定义的init方法执行之前首先执行 afterPropertiesSet方法
生命周期
5.自动装配
本来 给属性赋值,可以通过给属性自动赋值。
3. IOC[注解版]⚠️
1. 步骤
-
导包
-
开启包扫描
<!--需要将命名空间修改为以下内容-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
">
<context:component-scan base-package="com"></context:component-scan>
</beans>
- 在类上添加相应的注解
2.构造对象的注解?
序号 | 注解 | 通常用在 |
---|---|---|
1 | @Component[组件] | 实体类 |
2 | @Repository | dao层 |
3 | @Service | service层 |
4 | @Controller | 控制器层 |
3.给属性赋值的注解?
- @Autowired
- @Resource
@Autowired和@Resource的区别
1.@Autowired默认按照类型注入,如果同一个类型有多个实现,则可以按照名字注入,需要@Qualifier注解配合
2.@Autowired来自Spring-beans包,属于Spring自己的注解
3.@Resource来自javax.annotation包,默认按照名字,如果名字不匹配,也可以按照类型。
4. spring和WEB容器整合
- 让WEB容器启动的时候初始化IOC容器
- 配置监听器
<!--在WEB-INF/web.xml的web-app标签里增加以下内容-->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
- 配置配置文件的地址
<!--配置文件默认是 WEB-INF/applicationContext.xml ,若果修改配置文件的路径需要修改contextConfigLocation这个参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext_anno.xml
</param-value>
</context-param>
- 在servlet中可以直接获得ApplicationContext
WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getSErvletContext());
StudentServiceImpl service = stc.getBean("xxx",StudentSErviceImpl.class);
service.DOXXX();
5. 使用@Controller注解
使用@Controller注解可以不用配置监听器,是 4.spring与web容器整合的简化版,此处的内容属于spring-mvc的内容
- 首先在WEB-INF/web.xml中添加以下内容
<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:applicationContext_annotation.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 在applicationContext.xml中添加一下内容
加了mvc:annotation-driven标签之后,可以识别**@RequestMapping** 和 @ResponseBody
<!--首先将头部信息换成以下内容-->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!--新增内容 添加之后可以识别@RequestMapping @ResponseBody-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--视图解析器 在没有@ResponseBody的时候才有用-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/pages/"></property>
<property name="suffix" value=".html"></property>
</bean>
</beans>
- 控制器类
package com.etoak.student.controller;
import com.etoak.student.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/stu")
public class StudentController {
@RequestMapping("add")
@ResponseBody
/**
*这里的参数可以传任意值,可以放置一个对象,也可以分散着传递
*/
public Map add(Student stu){
System.out.println(stu.getName()+"\t"+stu.getAge()+"\t"+stu.getBirth());
Map<String,Object> map = new HashMap<>();
map.put("key",stu);
return map;
}
}
6.spring-mvc与mybatis整合
首要需要一个 mybatis-spring的maven依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.2</version> </dependency>
配置文件中添加配置
<!--mybatis--> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--配置数据源--> <property name="dataSource" ref="ds"></property> <!--给包起别名--> <property name="typeAliasesPackage" value="com.etoak.emp"></property> <!--映射文件位置--> <property name="mapperLocations" value="classpath:xml/*.xml"></property> <!--分页插件--> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageHelper"> <property name="properties"> <value> helperDialect=mysql </value> </property> </bean> </array> </property> </bean>
分页插件与spring结合
https://pagehelper.github.io/docs/howtouse/
配置日期转换器
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.etoak.emp.convertor.ETDateConvertor"></bean>
</set>
</property>
</bean>
package com.etoak.emp.convertor;
public class ETDateConvertor implements Converter<String,Date> {
@Override
public Date convert(String s) {
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
try{
return sdf.parse(s);
}catch(Exception e){
e.printStackTrace();
return null;
}
}
}
7.spring AOP ⚠️
首先AOP是 Aspect Oriented Programming[面向切面编程],是对OOP的一种补充,面向切面编程思想将程序分为两个部分。核心业务和非核心业务(给核心业务提供公共服务的),致力于解决核心业务和非核心也为在代码层面的分离
public class JayZhou(){
public void teach(){
//前置非核心业务
System.out.println("上课钱收手机");
//核心业务逻辑
System.out.println("认真的上课");
//后置非核心业务
System.out.println("下课解决问题")
}
}
可以通过代理模式实现核心和非核心业务的分离,被代理的对象为目标对象,代理生成的代理对象完成非核心业务,目标对象只需要关注核心业务即可
1. 静态代理
- 为要被代理的目标类型设计一个接口
- 被代理的目标类型实现这个接口,并且只关注核心业务
- 代理类型要和目标类型实现相同的接口
- 代理类型应该持有一个被代理的目标对象
- 代理类型只关注**非核心业务**
- 需要体现核心的时候,调用目标对象的方法
目标对象和代理对象要实现的接口
public interface Teacher{
public void teach();
}
目标对象实现接口
public class JayZhou implements Teacher{
@Override
public void teach(){
System.out.println("认真讲课");
}
}
代理类型
public class BigWhite implements Teacher{
private Teacher tea;
public BigWhite(Teacher tea){
this.tea = tea;
}
@Override
public void teach(){
//前置非核心业务
System.out.println("上课前收手机");
//核心业务
tea.teach();
//后置非核心业务
System.out.println("下课排错");
}
}
测试类
public class TestStaticProxy{
@Test
public void testProxy(){
Teacher db = new BigWhite(new JayZhou());
db.teach();
}
}
静态代理的缺点
我们要给每一个目标对象创建一个接口,还要代理对象实现该接口,如果有很多接口和很多方法的话,需要新建的东西就会成倍的增加,而且有很多重复的东西,很繁重,因此使用动态代理来创建出代理对象
2.动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HPRlqdwY-1602465857583)(https://ae04.alicdn.com/kf/H21c9b4aecdb04583a037665dfaa54fdel.png)]
动态代理不需要我们手动的创建代理类,通过代码生成我们要的代理类并返回,一般由两种形式,:
- 当目标对象继承了接口的情况下,可以使用JDK自带的Proxy类创建与目标对象实现相同接口的代理对象。
- 当目标没有实现接口的话,可以使用cglib这个第三方的类库来创建继承了目标对象的代理类对象。
当目标对象既没有实现接口又是final 类型的时候则会报错
通过代码生成的代理了对象通过类加载器再加载执行相关操作
1. JDK方式实现动态代理
公共接口
public interface Teacher{
public void teach();
}
实现了该接口的实现类
public class JayZhou implements Teacher{
@Override
public void teach(){
System.out.println("超级认真的讲课");
}
}
测试类
注意,使用JDK自带的代理类创建对对象的时候需要传三个参数
- 类加载器
我们可以通过使用目标类型的类加载器来加载创建出来的代理类
jay.getClass().getClassLoader();
- 需要实现的接口类型
因为一个类是可能实现多个接口的,所以这里需要放一个Class类型的数组 Class[] ,但是我们可以通过获取目标类型实现的接口,来让代理类实现这些接口
//new Class[]{...} jay.getClass().getInterfaces();
- 调用控制器
第三个参数需要一个实现了 InvocationHandler接口的实现类,这个接口里边规定了一个方法,可以作为我们实现非核心业务的载体。
public class TestJDKProxy{
public static void main(Stringp[] args){
JayZhou jay = new JayZhou();
Teacher db = (Teacher)Proxy.newProxyInstance(
jay.getClass().getClassLoader(),
jay.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method m,
Object[] args) throws Throwable {
//可以通过m对当前代理的方法进行各种判断
//从而提供不同的非核心注入
//可以通过判断args和修改args
//对调用方法的参数进行操作修改
System.out.println("==前置非核心:收手机==");
Object ok = m.invoke(jay, args);
System.out.println("==后置非核心:看自习==");
//将核心调用后的结果进行一定的统一处理后再返回~
return ok;
}
});
db.teach();
}
}
错误集锦
- 当 m.invoke()第一个参数填写成代理类型时
创建代理类的时候执行非核心的方法,传入一个代理类的对象,导致循环调用,会出错
- 无法解析成一个类
通过Proxy.newProxyInstance() 方法返回的类型应该强转为目标类型的接口类型,如果强转成目标类型则会报错
ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to com.etoak.aop.dynamicproxy.jdkver.JayZhou
2.通过cglib动态代理
使用 cglib 方式代理需要使用 cglib 第三方jar包,此种方式是创建了一个继承目标类型的子类对象,当目标类型是final类型的时候无法创建代理对象。可以引入一下maven依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.1_3</version> </dependency>
目标类型与接口同上
测试类
- 首先创建Enhancer类型的对象【增强器类型】
- 给Enhancer对象设置父类类型
- 设置非核心业务的载体 MethodInterceptor接口的实现类
使用cglib的好处,当我们获取不到目标类型的对象时可以在方法执行的时候可以传一个代理对象执行方法,
/** * 由原来的目标对象的方法被执行,替换成了代理对象的方法执行父类的方法 */ Object 0 = m.invok(jay,args); //替换为 Object o = mp.invokeSuper(proxy,args);
- 最后调用 create() 方法创建出来对象
package com.etoak.test;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.etoak.JayZhou;
public class TestCGLIBProxy {
public static void main(String[] args) {
JayZhou jay = new JayZhou();
//Enhancer => 增强器
Enhancer en = new Enhancer();
en.setSuperclass(jay.getClass());
//方法拦截器对象 等价于 InvocationHandler
en.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object proxy, Method m, Object[] args,
MethodProxy mp) throws Throwable {
System.out.println("==上课前 收手机==");
//Object ok = m.invoke(jay, args);
Object ok = mp.invokeSuper(proxy, args);
System.out.println("==下课后 看自习==");
return ok;
}
});//设置非核心业务的载体
JayZhou db = (JayZhou)en.create();
db.teach();
}
}
3.spring 版本的AOP[xml版本]
spring 底层使用了以上两种动态代理的方式,会根据目标类型的状态自动的选择怎么创建代理类对象,当目标类型 既没有继承接口又是final类型的类 时无法创建代理类对象,报错
配置spring.xml
expression 表达式中的含义
execution(* *.*.*.*(..))
第一个 * 是方法的返回类型
最后一个 * 是方法名
写成 com…ET.teach() 代表com下的任意自包的ET类的teach方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 配置那条鱼 -->
<bean id="jay" class="com.etoak.JayZhou" />
<!-- 配置那片姜 -->
<bean id="jp" class="com.etoak.advice.EtoakBeforeAdvice" />
<bean id="ljp" class="com.etoak.advice.EtoakAfterAdvice" />
<!-- AOP配置开始 -->
<aop:config>
<!-- 切入点的配置 在鱼身上划开切口 -->
<aop:pointcut id="dk1"
expression="execution(* *..*.*(..))" />
<aop:advisor advice-ref="jp" pointcut-ref="dk1" />
<aop:advisor advice-ref="ljp" pointcut-ref="dk1" />
</aop:config>
</beans>
3.1 通知的类型
- 前置通知
前置非核心需要继承 MethodBeforeAdvice 接口,
参数 描述 Method 要执行的方法 Object[] args 方法执行所需要的参数 Object target 目标对象
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class EtoakBeforeAdvice implements MethodBeforeAdvice {
//非核心业务的载体
@Override
public void before(Method m, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("前置非核心:收手机");
}
}
- 后置通知
需要继承的接口为 AfterReturningAdvice 方法返回前插入操作
参数 描述 Object proxy 方法执行之后返回的就结果 Method m 要执行的方法 Object[] args 方法需要用的参数 Object target 目标对象3
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class EtoakAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object proxy, Method m, Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
System.out.println("后置非核心:下课找错误 ");
}
}
- 环绕通知
可以有选择的执行核心方法,需要继承 aopallience包下的MethodInterceptor方法,当我们需要执行核心方法的时候 需要调用 proceed(), 通过形参可以获取到目标对象的方法名称,来判断是否需要
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class EtoakAroundAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
String methodName = mi.getMethod().getName();
System.out.println("AroundAdvice的前置通知");
/*
*if(methodName.startWith("sleep")){
}
*/
Object target = mi.proceed();
System.out.println("AroundAdvice的后置通知");
return target;
}
}
- 异常通知
异常通知只有程序运行出现异常的时候才会注入,可以用来输出错误日志,实现的是ThrowsAdvice这个接口,这个接口里边并没有定义抽象方法,但是我们需要注意的是 自己需要记住
public void afterThrowing(Exception e){} public void afterThrowing(Method m,Object[] args,Object target,Exception e){}
该接口由两个重载的方法,我们可以使用第一个只带有错误信息的,也可以使用第二个带有详细信息的方法。最后在测试类里调用能够抛出遗产的方法即可
//给测试的JayZhou方法加上一个能够抛出异常的方法
public void wc() throws Exception{
int i = (int)(Math.random()*2);
if(i == 1){
thorw new Exception("没有厕所啦");
}
System.out.println("神经气爽");
}
package com.etoak.advice;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import org.springframework.aop.ThrowsAdvice;
public class EtoakThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Method m,Object[] args,Object target,Exception e) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try (PrintWriter out = new PrintWriter(new FileWriter("D:\\err.log",true),true)){
out.println(target + "在北京时间" + sdf.format(System.currentTimeMillis()) + "尝试调用"
+ m.getName() + "方法时出现了异常,异常信息是:" + e.getMessage());
}catch(Exception ee) {
ee.printStackTrace();
}
}
}
4. spring AOP[注解版本]
需要的是spring-aop 和spring-ioc的核心jar包
4.1前期配置
- 创建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
">
<!-- 告诉spring IOC中的组件 扫描注解 -->
<context:component-scan base-package="com"></context:component-scan>
<!-- 让AOP的配置也去扫描注解-->
<!-- proxy-target-class=true 使用cglib方式创建代理类 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
- 给目标类型添加注解
@Component("jay")
public class JayZhou{
//方法体内容同上xml版
public void taech(){}
public void wc()throws Exception{}
}
- 创建Aop配置类
注意: @Component 和 @Aspect 直接缺一不可
@Pointcut(“execution()”) 这里对应的是xml配置文件里的表达式,要注入哪个方法
package com.etoak.aop;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AopConfig {
@Pointcut("execution(* *..*.teach(..))")
public void teach() {}
@Pointcut("execution(void com.etoak.aop.JayZhou.wc())")
public void wc() {}
@Before("teach()")
public void first() {
System.out.println("==前置非核心:上课收手机==");
}
@After("teach()")
//@AfterReturning("teach()")
public void last() {
System.out.println("==后置非核心:下课排BUG==");
}
@Around("teach()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
String methodName = pjp.getSignature().getName();
Object tar = pjp.getTarget();
System.out.println(methodName + "\t" + tar);
System.out.println("==环绕通知前置==");
Object ok = pjp.proceed();
System.out.println("==环绕通知后置==");
return ok;
}
@AfterThrowing(value="wc()",throwing="e")
public void help(JoinPoint jp,Exception e) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String m = jp.getSignature().getName();
Object target = jp.getTarget();
try (PrintWriter out = new PrintWriter(new FileWriter("D:\\err.log",true),true)){
out.println("北京时间:" +sdf.format(System.currentTimeMillis()));
out.println(target + "尝试调用" + m + "方法时出现了异常。");
out.println("异常简略信息为:" + e.getMessage());
out.println("---------------------------------");
}catch(Exception ee) {
e.printStackTrace();
}
}
}
4.2 spring AOP注解
- @Aspect与@Component
告诉IOC与AOP容器,这是一个aop的配置文件**缺一不可**
- @Pointcut
切点里边要填写expression表达式,要切入哪个方法
//表示要切入 com下的任意层级子包下的JayZhou类的void teach方法
@Pointcut("execution(void com..JayZhou.teach())")
//这个方法名字对边写,只需要下边用到通知的时候一致即可
public void test(){}
- @Before()
前置通知 ,放置前置非核心业务,这里的值需要与上边切入点的方法名保持一致
选用参数 JoinPoint 当前的切入点,可以获取到当前执行方法的一些信息
@Before("test()")
public void first(){
//前置非核心
}
/*
public void first(JoinPoint jp){
//获取方法名
String methodName =jp.getSignature().getName();
//获取目标类型
Object tar = jp.getTarget();
//获取参数
Object[] atgs = jp.getArgs();
}
*/
- @After
后置通知 后边的属性值要与切入点的方法名称保持一致
这个注解不管执行的方法是不是抛出异常都会执行注入操作
@After("test()")
public void last(){
//后置非核心
}
- @AfterReturning
后置通知 当方法返回的之后才执行注入操作,如果方法运行时抛出了异常信息怎不进行注入操作
@AtferReturning("test()")
public void last(){
//后置非核心
}
- @Around
环绕通知 当中有必选参数 ProceedJoinPoint,只有有了这个参数才可以获取到核心逻辑,去执行核心的代码
@Around("teach()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
String methodName = pjp.getSignature().getName();
Object tar = pjp.getTarget();
System.out.println(methodName + "\t" + tar);
System.out.println("==环绕通知前置==");
Object ok = pjp.proceed();
System.out.println("==环绕通知后置==");
return ok;
}
- @AfterThrowing
异常通知 当方法出现异常的时候进行注入操作
注意: 如果要打印日志信息的话,需要获取到一场信息等,在方法参数里边添加Exception e 之后需要在注解里绑定抛出的异常的参数 也是就是 throwing=“e” ,因为有两个参数了,所以value就得书写,如果是一个参数则value可以省略
如果throwing没写,则会报错
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
@AfterThrowing(value="wc()",throwing="e")
public void help(JoinPoint jp,Exception e) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String m = jp.getSignature().getName();
Object target = jp.getTarget();
try (PrintWriter out = new PrintWriter(new FileWriter("D:\\err.log",true),true)){
out.println("北京时间:" +sdf.format(System.currentTimeMillis()));
out.println(target + "尝试调用" + m + "方法时出现了异常。");
out.println("异常简略信息为:" + e.getMessage());
out.println("---------------------------------");
}catch(Exception ee) {
e.printStackTrace();
}
}
错误集锦
- Error creating bean with name ‘jay’ defined in file [D:\Study\Yitu\et1912_workspace\spring_AOP_annotation\bin\com\etoak\aop\JayZhou.class]: Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: warning no match for this type name: com.eroak.aop.JayZhou [Xlint:invalidAbsoluteTypeName]
引发原因:配置配置文件的etoak写成了eroak
第十一章 Echarts的使用
-
需要的就是文件 echarts.js
具体事宜需要自己好好的分析
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>单车列表</title> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/bootstrap-table.css"> </head> <body> <div id="app"> <!-- 添加单车模态框 --> <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="headingOne"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> 租赁中的数据统计 </a> </h4> </div> <div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne"> <div class="panel-body"> <table id="tb"> </table> </div> </div> </div> </div> <div class="panel-group" id="accordion1" role="tablist" aria-multiselectable="true"> <div class="panel panel-default"> <div class="panel-heading" role="tab" id="headingOne1"> <h4 class="panel-title"> <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> 租赁中的数据统计 </a> </h4> </div> <div id="collapseOne1" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne1"> <div class="panel-body"> <div id="main" style="width: 1000px;height:400px;"></div> </div> </div> </div> </div> <!-- <div id="echarts" style="width: 1000px;height: 200px;"></div>--> </div> </body> <script src="./js/jquery.min.js"></script> <script src="./js/bootstrap.min.js"></script> <script src="js/bootstrap-table.min.js"></script> <script src="js/bootstrap-table-zh-CN.js"></script> <script src="js/ajaxfileupload.js"></script> <script src="js/echarts.js"></script> <script> $(function(){ queryData(); showData() }) function showData() { let myChart = echarts.init(document.getElementById('main')) let option = { tooltip: { trigger: 'axis', axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' } }, legend: { data: [] }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: [ { type: 'category', data: [] } ], yAxis: [ { type: 'value' } ], series: [ ] } $.post('bike',{method:'tongji'},msg=>{ let data_lengends = [] let data_xs = [] msg.forEach(rent=>{ data_lengends.push(rent.BRAND) data_xs.push(rent.SDATE) }) data_lengends = Array.from(new Set(data_lengends)) data_xs = Array.from(new Set(data_xs)) console.log(data_lengends) console.log(data_xs) let data_series = [] $.each(data_lengends,function (i,brand) { let s_date = {} s_date.name = brand; s_date.type = 'bar' let data1 = [] $.each(data_xs,function (i,date) { let flag = false $.each(msg,function (i,tj) { let d1 = tj.SDATE let b1 = tj.BRAND if(brand == b1 && date == d1){ flag = true data1.push(tj.NUM) } }) if(!flag){ data1.push(0) } }) s_date.data = data1 data_series.push(s_date) }) option.legend.data = data_lengends option.xAxis[0].data = data_xs option.series = data_series console.log(option) myChart.setOption(option) },'json') // 指定图表的配置项和数据 // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); } function queryData() { $('#tb').bootstrapTable({ url:'bike?method=tongji', columns:[{ title:'日期', field:'SDATE' },{ title:'单车型号', field:'BRAND' },{ title:'数量', field:'NUM' }] }) } </script> </html>
第十二章 练习搭框架
1. 创建maven工程,引入一下依赖
<!--常用的jar包-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
<!--因为spring 项目没有了config.xml文件,所以在pom.xml的build标签内将代码生成器的插件引入-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
</plugin>
2.创建配置文件
- 在main包下创建 java目录 和 resource目录
1. 在resource目录下创建applicationContext.xml文件
- 导入外部的配置文件
<!--用配置文件的方式引入外部的数据库配置文件,方便更换数据库-->
<!--此处如果忘记书写classpath则会报错 can not open spring context ... 找不到db.properties文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
- 过滤静态资源
<!--不开启静态资源过滤的话会导致HTML等页面404-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
- 需要修改的地方有
- 包扫描改为自己的顶层包
- mybatis映射文件的地址改为自己新的路径
- 全部的配置文件为
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!--导入外部的数据库的配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--包扫描 -->
<context:component-scan base-package="com.etoak.emp"></context:component-scan>
<!--静态资源过滤 -->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
<!--文件上传-->
<
<!--ds-->
<bean id="ds" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${m.driver}"></property>
<property name="url" value="${m.url}"></property>
<property name="username" value="${m.user}"></property>
<property name="password" value="${m.pwd}"></property>
</bean>
<!--mvc @RequestMapping @Responbody-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.etoak.emp.convertor.ETDateConvertor"></bean>
</set>
</property>
</bean>
<!--视图解析器 add(){return "/pages/et.html"}-->
<!--
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".html"></property>
</bean>
-->
<!--文件上传-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean>
<!--mybatis-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="ds"></property>
<!--给包起别名-->
<property name="typeAliasesPackage" value="com.etoak.emp"></property>
<!--映射文件的位置-->
<property name="mapperLocations" value="classpath:xml/*.xml"></property>
<!--分页插件-->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<!--使用下面的方式配置参数,一行配置一个 -->
<value>
helperDialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!--@Controller @Service -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.etoak.emp.mapper"></property>
</bean>
</beans>
- 创建数库配置文件和代码生成器的配置文件
##数据库配置文件 对应上边de db.properties
m.driver=com.mysql.jdbc.Driver
m.url=jdbc:mysql:///et1912
m.user=root
m.pwd=
代码生成器配置详见第五章第五节
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!--mysql版本的配置 ,详情参考第五章第五节代码生成器配置-->
<generatorConfiguration>
<classPathEntry location="D:/mysql.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--此处填写jdbc的驱动-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql:///et1912"
userId="root"
password="dream">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--实体类生成的包路径-->
<javaModelGenerator targetPackage="com.etoak.emp.entity"
targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<sqlMapGenerator targetPackage="xml"
targetProject="src/main/resources">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!--Mapper生成的路径-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.etoak.emp.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table schema="scott" tableName="emp"
domainObjectName="Emp" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="empno" sqlStatement="Mysql" identity="true" />
</table>
<table schema="scott" tableName="dept"
domainObjectName="Dept" >
<property name="useActualColumnNames" value="false"/>
<!--主键 <selectKey keyProperty="empno" resultType=""> </selectKey>-->
<generatedKey column="deptno" sqlStatement="Mysql" identity="true" />
</table>
</context>
</generatorConfiguration>
- log4j.peoperties
### set log levels ###
log4j.rootLogger = debug,stdout,D,E
### 输出到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} ===== %5p %c{1}:%L - %m%n
#### 输出到日志文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/log.log
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#
#### 保存异常信息到单独文件 ###
#log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
#log4j.appender.D.File = logs/error.log ## 异常日志文件名
#log4j.appender.D.Append = true
#log4j.appender.D.Threshold = ERROR ## 只输出ERROR级别以上的日志!!!
#log4j.appender.D.layout = org.apache.log4j.PatternLayout
#log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
2.配置web.xml
配置 web.xml 过滤字符编码集以及开始@Controller注解
<?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_3_1.xsd"
version="3.1">
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
<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>
<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:applicationContext.xml</param-value>
</init-param>
<!--是spring mvc在开始的时候就加载,防止后边的启动的太慢-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
最后配置tomcat,注意不要配置工程名
发送请求路径由原来的** 拦截地址?method=方法名&k=v** 更改为 /拦截地址/方法名?k=v
3. 错误集锦
- java.lang.NoClassDefFoundError: org/springframework/dao/support/DaoSupport
引发原因:没有导入spring-jdbc依赖
解决办法:在pom.xml中添加以下依赖
<!--spring_jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
- Each converter object must implement one of the Converter, ConverterFactory, or GenericConverter interfaces
引发原因:转换日期的类没有继承Convert接口
解决办法:将转换日期的类写全
import org.springframework.core.convert.converter.Converter;
public class DateUtils implements Converter<String,Object> {
@Override
public Object convert(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
return sdf.parse(s);
}catch(Exception e){
e.printStackTrace();
return null;
}
}
}
- 日期转换器错误
unparseable date for “xml” application/json application/xml
引发原因:未知
解决办法:重新写一遍日期转换代码
package com.test.emp.utils;
import org.springframework.core.convert.converter.Converter;
import java.text.SimpleDateFormat;
import java.util.Date;
//写DateUtils就不能用就很奇怪
public class DateConvertor implements Converter<String,Date> {
@Override
public Date convert(String s) {
SimpleDateFormat sdf= new SimpleDateFormat("yyyy-MM-dd");
try{
return sdf.parse(s);
}catch(Exception e){
e.printStackTrace();
return null;
}
}
}
4. 需要修改的地方
- 发送请求的地方
以前发送请求是需要 url/method=?&k=v
现在是 contoller/method/k=v
$.post(`emp`,`method=addEmp&k=v&k2=v2`,msg=>{
},'json')
/*现在修改为以下的样式*/
$.post(`/emp/addEmp`,`k=v&k1=v1..`,msg=>{
},'json')
- 文件上传
- 首先需要在applicationContext.xml中开启文件上传
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
- 在需要文件上传的方法形参里加上相应的参数
@RequestMapping("/addPic")
public ETJSONResponse addEmp(MultipartFile pic,Emp d,HttpServletRequest request, HttpServletResponse resp)throws Exception{
System.out.println(pic);
String fEXT = FilenameUtils.getExtension(pic.getOriginalFilename());
ServletContext application = request.getSession().getServletContext();
String newName = UUID.randomUUID().toString().replaceAll("-","")
+"."+fEXT;
String path = application.getRealPath("/files/"+newName);
pic.transferTo(new File(path));
//
//Bookpic pic = new Bookpic()
// ..
ETJSONResponse response = new ETJSONResponse();
if(d!=null && !StringUtils.isNullOrEmpty(d.getEname())) {
es.add(d);
response.setCode(2000);response.setStatus("ok");response.setMsg("添加成功!");
}else{
response.setCode(3000);response.setStatus("error");response.setMsg("参数为空!");
}
return response;
}
- 前端js
$.ajaxFileUpload({
url:"/emp/addEmp",
fileElementId:"pic",
data:{name:"李四",job:"市场经理",sal:9999,hiredate:"2020-09-09"},
dataType:"json",
success:function(msg){
}
})
小拓展
1.在前端将时间戳转换成字符串类型
<script>
function toString(value){
if(value != null) {
let time = new Date(value) //先将时间戳转为Date对象,然后才能使用Date的方法
let year = time.getFullYear()
let month = time.getMonth() + 1 //月份是从0开始的
let day = time.getDate()
let hour = time.getHours()
let minute = time.getMinutes()
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute
}
return ''
}
</script>