一年菜鸟,Java开发面试题总结


Java基础

1、JRE和JDK和JVM的区别?

JRE是java程序运行标准环境;JDK包含JRE它是java程序开发最小环境;JVM是Java虚拟机,java程序需要在虚拟机上运行,不同平台有不同JVM,实现java程序跨平台。

 

2、javase,javaee,javame的区别?

java分为三个版本,标准版javase,企业版javaee,微型版javame

 

3、JDK和SDK的区别?

SDK(Software Development Kit)软件开发工具包,相当广泛的名词是一系列文件的组合;JDK(JAVA Development Kit)JAVA开发工具包,针对java开发的。

 

4、Java八大基本数据类型?

数据类型 所占字节
byte 1字节,8比特位
short 2字节,16位
int 4字节,32位
long 8字节,64位
float 4字节,32位
double 8字节,64位
boolean true/false
char 2字节,16位

 

5、面向对象的三个特征?

封装、继承、多态。

封装:对象属性私有,对外提供一个公共访问的方法。好处是提高代码安全性,实现代码组件化。

继承:子类继承父类,无需重新编写实现代码功能的扩展。

多态:父类的引用指向子类的对象。多态存在的三个必要条件(继承,重写,父类的引用指向子类的对象)

 

6、什么是父类的引用指向子类的对象?

即声明的是父类,实际指向的是子类的一个对象。

优点可以用这几个关键词来概括:多态、动态链接,向上转型

 

7、Java中访问修饰符?

private:同一类中可见

dufault:同一包下可见

protected:同包及其子包下可见

public:对所有类可见

 

8、&,&&,|,|| 的区别?

 

 9、Java中运算符分为几种?

分为算数运算符和逻辑运算符。

 

10、面向对象的优点?

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

缺点:性能比面向过程低

 

11、面向对象的五大基本原则?

①单一职责原则:类的功能要单一。

②开放封闭原则:一个模块对于扩展是开放的对于修改是封闭的。

③里式替换原则:子类可以替换父类出现在父类能够出现的任何地方。

④依赖倒置原则:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

⑤接口分离原则:设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。

 

12、抽象类和接口的区别?

抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。

从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。

相同点:

①接口和抽象类都不能实例化

②都位于继承的顶端,用于被其他类实现或继承

③都包含抽象方法,其子类都必须覆写这些抽象方法

不同点:

①抽象类可以有构造器,接口不可以

②子类用extends继承,用implements实现

③一个类只能继承有个抽象类,但可以实现多个接口

 

13、局部变量和成员变量的区别?

①局部变量定义在方法中,成员变量定义在类内部方法外部。

②成员变量随着对象的创建而创建随着对象的消失而消失,存储在堆内存中;局部变量当方法被调用时存储在栈内存中,方法调用完自动释放。

 

14、重载和重写的区别?

构造器不能被重写,但可以被重载。

重载:方法名相同,参数列表不同。

重写:发生在父子类中,方法名和参数列表必须相同。

 

15、“==” 和 equals的区别?

equals比较的是值相同;“==”当比较的是基本数据类型比较的是值,当是引用类型比较的是内存地址。

 

16、Java获取反射的三种方法?

  1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

//方式一(通过建立对象)
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
//方式二(所在通过路径-相对路径)
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
//方式三(通过类名)
Class classobj3 = Student.class;
System.out.println(classobj3.getName());

 

 

17、Java集合中有序无序的概念。

有序、无序是指在进行插入操作时,插入位置的顺序性。先插的位置在前,后插的位置在后,则为有序,反之无序

集合  有序无序
实现List接口的 ArrayList、LinkedList有序
实现Set接口的 HashSet无序、TreeSet有序
实现Map  HashMap无序,TreeMap有序

 

18、有关HashMap

HashMap数组初始长度为16,HashMap只允许一个key为null,允许多个value为null。

HashMap扩容分为两步:第一步把数组长度变为原来的两倍,第二步采用头插法,把旧的数组的元素插入到新数组中。

HashMap大小为什么是2的幂次方:避免造成浪费和不随机等为题。

为什么负载因子0.75:负载因子过大空间利用率上升但是时间效率下降了,过小则反之。

 

19、Map.put(k,v)实现原理:先将k,v封装到node对象中(节点),然后根据k的hashCode()方法得出hash值,

然后通过哈希表函数将hash值转为数组下标,如果下标位置没有任何元素九八node添加上去,

如果对应的下标位置上有链表,则拿k对每个链表上的节点k进行equals如果返回true则节点上的value将会被覆盖,如果返回都是false则在链表末尾添加新的节点。

 

 Spring框架面试题

1、什么是Spring

Spring是一种轻量级框架,一般说的Spring框架就是指Spring Framework,它是很多模块的集合,这些模块分别是:核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。

Spring的6个特征:

核心技术:依赖注入(DI),AOP,事件(Events),资源,i18n,验证,数据绑定,类型转换,SpEL。

测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。

数据访问:事务,DAO支持,JDBC,ORM,编组XML。

Web支持:Spring MVC和Spring WebFlux Web框架。

集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。

语言:Kotlin,Groovy,动态语言。

 

2、谈谈对Spring IOC和AOP的理解?

IOC(控制反转,依赖注入)是一种设计思想,就是将原本在程序中手动创建的对象的控制权,交给Spring容器管理。将对象之间的依赖交给IOC容器

管理,由IOC容器完成对象的注入,简化应用开发,需要创建一个对象时,只需要配置好文件/注解即可。降低代码之间的耦合度

增加了项目的可维护性,降低了开发难度。

AOP面向切面编程,可以通过预编译方式和运行期间动态代理,在不修改源码的情况下给程序动态扩展功能的一种技术。

如果要代理的对象实现了某个接口,则Spring AOP就会用JDK动态代理去创建代理对象,没有实现接口的对象,无法使用JDK

动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

 

3、Spring中bean的生命周期?

初始化--》使用--》销毁

 

4、Spring框架中用到了哪些设计模式?

  • 工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。
  • 代理设计模式:Spring AOP功能的实现。
  • 单例设计模式:Spring中的bean默认都是单例的。
  • 模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。

5、Spring的事务管理方式有几种?

  编程式事务:编程方式管理事务,极大灵活性,难维护。

  声明式事务:可以将业务代码和事务管理分离,用注解和xml配置来管理事务。

 

6、spring注入bean的几种方式?

构造器,setter方法,注解

 

SpringMVC框架面试题

1、说说对SpringMVC的了解

MVC是一种设计模式,在SpringMVC下主要可以把后端项目分为,Controller控制层,Service业务层,Dao持久层和实体类。

 

2、Spring MVC的工作原理?

  1. 客户端(浏览器)发送请求,直接请求到DispatcherServlet。
  2. DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。
  3. 解析到对应的Handler(也就是我们平常说的Controller控制器)。
  4. HandlerAdapter会根据Handler来调用真正的处理器来处理请求和执行相对应的业务逻辑。
  5. 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。
  6. ViewResolver会根据逻辑View去查找实际的View。
  7. DispatcherServlet把返回的Model传给View(视图渲染)。
  8. 把View返回给请求者(浏览器)。

3、Spring MVC中的主要组件

  前端控制器DispatcherServlet

  处理映射器HandlerMapping

  处理适配器HandlerAdapter

  处理器Handler(控制器)

  视图解析器ViewResolver

  试图View(jsp)

 

4、Spring MVC怎样设定重定向和转发?

  forward、redirect

5、如何开启注解处理器和适配器?

  配置文件中添加<mvc:annotation-driven>实现

 

MyBatis框架面试题

 1、${} 和 #{} 的区别?

#传入的参数都会当成一个字符串,会对传入的数据加上引号,$传入的参数是直接显示生成在sql中的被当作一个对象,

#方式 它底层采用预编译方式PerparedStatement,很大程度上能够防止sql注入问题,$方式 底层使用Statement,无法

方式sql注入问题。在传数据库名/表名/字段名时使用$。

 

2、MyBatis是如何将sql执行结果封装为目标对象并返回的?

mybatis有两种映射,一种是resultType,另一种是resultMap。

resultType也叫自动映射,对象属性和数据表字段名必须一致才能映射成功,不一致时可以在sql中取列别名。

resultMap也叫手动映射,可以手动将实体类属性和数据表字段一一对应。

 

3、如何开启MyBatis的延迟加载?

延迟加载又叫做按需加载,就是当用到需要的数据的时候才会执行查询操作,减轻了数据库的压力。

mybatis的延迟加载功能默认是关闭的,需要在xml文件中通过setting标签来开启延迟加载功能。

 lazyLoadingEanbled:全局性设置懒加载,true

aggressiveLazyLoading:false

<settings>
  <setting name ="aggressiveLazyLoading" value="false"/>
  <!--开启延迟加载-->
  <setting name="lazyLoadingEnabled" value="true"/>
</settings>

 

 

4、MyBatis支持延迟加载的原理?

使用CGLIB创建目标对象的代理对象,然后调用目标方法时,进入拦截器方法。比如调用对象的get方法获取对象属性时,

拦截器的invoke()方法发现get方法获取的是nulll值,然后就会单独发送事先保存好的查sql,把之前需要的属性查询出来,

然后用set方法赋值,于是在前一个对象属性就有值了。最后调用对象的.getB().getName()方法。

 

5、JDBC编程的不足有哪些,mybatis是如何解决的?

JDBC创建数据库的连接,频繁释放造成系统资源浪费影响了性能,而且sql语句写在代码中不易维护。

mybatis将sql语句与java代码分离,能够将数据库记录封装成java对象,mybatis能够自动将sql结果有哪个映射到java对象。

 

6、MyBatis的一级缓存和二级缓存?

mybatis一级缓存指的是SQLSession,一级缓存的作用域是SQLSession。

mybatis默认开启一级缓存,在同一个sqlSession中执行相同的sql查询时,第一次会查询数据库,并写在缓存中。

第二次会直接从缓存中取,当两次查询之间发生增删改的操作时,则sqlSession的缓存会被清空。每次查询前去缓存中找,

如果找不到再去查数据库。mybatis的内部缓存使用的是HashMap,key值为hashCode+statementId+sql语句,value值为

查询出来的结果集映射成的java对象。

mybatis默认没有开启二级缓存,二级缓存指的是mapper映射文件,作用域是同一个namespace下。

开启二级缓存:

cacheEnabled设置为true;对应的mapper.xml文件中需要使用<cache/>标签

二级缓存,第一次查询信息会存放在mapper对应的二级缓存中,第二次相同的sql查询会去对应的二级缓存内取结果,

如果调用了相同的namespace下的增删改sql并执行了commit操作,此时缓存会被清除。

 

7、MyBatis编程步骤是什么?

  1. 创建SqlSessionFactory工厂(Mybatis工厂)
  2. 通过工厂创建SqlSession.
  3. SqlSession执行数据库操作.
  4. Session.commit()提交事务.
  5. Session.close()关闭会话.

 

SpringBoot框架面试题

1、什么是SpringBoot?

用来简化Spring应用的初始搭建以及开发过程,使用特定的方式来进行配置,properties或yml文件。

创建独立的Spring引用程序main方法运行。

嵌入式tomcat无需部署war文件

简化了maven配置

starter自动化配置

 

2、SpringBoot自动配置原理?

SpringBoot遵循“约定优于配置”的原则。

在入口类中使用@SpringBootApplication注解来启动整个应用,它是SpringBoot的核心注解,

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

public @interface SpringBootApplication {
    
}

 

上面注解里最主要的就是@EnableAutoConfiguration(开启自动装配)

再进入@EnableAutoConfiguration的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)

public @interface EnableAutoConfiguration {
    
}

 

可以看到@EnableAutoConfiguration注解内部使用了@Import注解来完成导入配置的功能。

EnableAutoConfigurationImportSelector它使用SpringFactoriesLoader.loadFactoryNames方法,用来扫描具有

MEAT-INF/spring.factories文件的jar包,它的里面都是自动配置类,SpringBoot会根据这些自动配置类去自动配置环境。

spiringboot项目启动时,会执行selectImport方法,找到自动配置类对相应的class然后将所有自动配置类加载到Spring容器中

spring.factories文件是key,value形式的。

 

3、SpringBoo核心配置文件是什么?

bootstrap(.yml或.properties),由ApplicationContext加载比application文件优先加载,且里面的属性不能被覆盖。

application(.yml或.properties),用于spring boot项目的自动化配置。

 

 

SSM框架和SpringBoot框架的区别

 1、SpringBoot、Spring MVC、Spirngde区别?

SpringFrame

SpringFramework 最重要的特征是依赖注入。所有 SpringModules 不是依赖注入就是 IOC 控制反转。

当我们恰当的使用 DI 或者是 IOC 的时候,我们可以开发松耦合应用。松耦合应用的单元测试可以很容易的进行。

SpringMVC

Spring MVC 提供了一种分离式的方法来开发 Web 应用。通过运用像 DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。

SpringBoot

Spring 和 SpringMVC 的问题在于需要配置大量的参数,Spring Boot 通过一个自动配置和启动的项来目解决这个问题。

为了更快的构建产品就绪应用程序,Spring Boot 提供了一些非功能性特征。

 

MySQL数据库面试题

 1、什么是sql?

结构化查询语言,是一种数据库查询语言。用于存储数据、查询、更新、和管理关系型数据库。

 

2、什么是MySQL?

它是企业级开发中非常常用的,开源的关系型数据库。

 

3、数据库的三大范式?

第一范式:每列保存原子性,每个列都不可以再拆分。

第二范式:在第一范式的基础上,第二范式确保表中的每列都和主键相关,而不能只与主键的一部分相关,这是针对联合主键

而言,也就是说每一个数据表中只能保存一中数据。

第三范式:在第二范式的基础上,非主键只能依赖于主键,不依赖于其他非主键。就是说确保每列都和主键列直接相关,而不是

间接相关,避免冗余列。

 

4、MySQL有关权限的表有哪几个?

  • user权限表:记录允许连接到服务器的用户帐号信息,里面的权限是全局级的。
  • db权限表:记录各个帐号在各个数据库上的操作权限。
  • table_priv权限表:记录数据表级的操作权限。
  • columns_priv权限表:记录数据列级的操作权限。
  • host权限表:配合db权限表对给定主机上数据库级操作权限作更细致的控制。这个权限表不受GRANT和REVOKE语句的影响。

5、mysql分为哪几种数据类型?

  • 整数类型
  • 小数类型
  • 字符串类型
  • 枚举类型
  • 日期和时间类型 

 

6、MySQL常用存储引擎有哪些?

  • Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
  • MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
  • MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。

7、MySQL存储引擎MyISAM和InnoDB的区别?

  • InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
  • InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
  • MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
  • InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。

  存储引擎的选择:

    如果没有特别的需求,使用默认的Innodb即可。

    MyISAM:以读写插入为主的应用程序,比如博客系统、新闻门户网站。

    Innodb:更新(删除)操作频率也高,或者要保证数据的完整性;并发量高,支持事务和外键。比如OA自动化办公系统。

8、什么是索引?

  索引通俗的说相当于目录,它们包含着对数据表里所有记录的引用指针;

  索引的数据结构是B树及其变种B+树。

  优点:大大增加检索速度提高系统性能

  缺点:创建索引耗费时间,对表中进行增加、删除修改操作时,索引也需要动态维护。降低增删改的执行效率。

 

9、索引有哪几种类型?

  • 主键索引
  • 唯一索引
  • 普通索引
  • 全文索引

10、事务的四个属性ACID?

  1、原子性(atomicity

  事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用

  2、一致性(consistency)

  一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中

  3、隔离性(isolation)

  可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏

  4、持久性(durability)

  事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

11、什么是不可重复读?幻读?脏读?

  不可重复读:两次查询的数据不一致

  幻读:两次查询的记录条数不一致

  脏读:数据在更新时读取到了错误的数据,未更新成功,读取到了更新后的数据,事务出错回滚了。

12、事务的隔离级别?

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

 

有关Redis面试题

 1、Redis支持的数据类型?

  1.String字符串  

    格式:set key value

  2.Hash(哈希)

    格式:hmset name key1 value1 key2 value2...

  3.List(列表)

    Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

    格式: lpush  name  value

    在 key 对应 list 的头部添加字符串元素

    格式: rpush  name  value

    在 key 对应 list 的尾部添加字符串元素

    格式: lrem name  index

    key 对应 list 中删除 count 个和 value 相同的元素

    格式: llen name  

    返回 key 对应 list 的长度

  4.Set(集合)

    格式: sadd  name  value

    Redis的Set是string类型的无序集合。

    格式: zadd  name score value

    Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。

2、什么是缓存穿透,缓存击穿,缓存雪崩?

  • 缓存穿透:在缓存系统中,首先是按照key去缓存查询,如果不存在则去数据库中查询,如果恶意大量的去查询不存在的key,会造成数据库的压力,缓存失去了原本的意义,这就是缓存穿透。
  • 缓存击穿:高并发访问热点key,这时候可能会出现缓存击穿。
  • 缓存雪崩:当缓存服务器重启或者说某一时间内大量key过期了,这样所有请求会直接访问数据库,给数据库造成很大压力。

 

3、如果解决缓存穿透,缓存击穿,缓存雪崩?

  缓存穿透:采用布隆过滤器,将所有可能存在的数据hash到一个足够大的bitmap中,

    一个一定不存在的数据会被这个bitmap拦截;或者将查询为空的数据进行缓存,

    设置一个短的过期时间。

  缓存击穿:业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),

    不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)

    去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

  缓存雪崩: 那就要在存储的时候,设置有效时间的时候,在失效时间的基础上增加一个Random随机值,不让大量key

    在同一时间过期。

 

Shiro框架面试题

 1、什么是shiro?

  Shiro是一个强大易用的java安全框架,提供了认证、授权、加密、会话管理、与web集成、缓存等功能,对于任何一个应用程序,都可以提供全面的安全服务,相比其他安全框架,shiro要简单的多。

2、shiro的核心组件Subject、SecurityManager、Realms

  1,Subject:主体,代表了当前用户,与当前应用交互的所有东西都是Subject,如人、爬虫、机器人等。所有Subject都绑定

    SecurityManager,与Subject所有的交互都会委托给SecurityManager,它才是实际的执行者。

  2,SecurityManager:安全管理器,所有有关安全的操作都会与SecurityManager交互,并且它管理所有的Subject,

    它是shiro的核心。

  3,Realm:域,shiro从Realm中获取安全数据(如用户、角色、权限),当SecurityManager验证用户身份时,那么它

    需要从Realm获取相应的用户进行比较以确定用户身份是否合法。也需要从Realm中得到用户相应的角色、权限进行

    验证用户是否有权限进行操作。可以把Realm看成DataSource,即安全数据源。

 3、shiro框架的优点

  • 简单的身份验证,支持多种数据源
  • 对角色的简单授权,支持细粒度的授权(方法)
  • 支持一级缓存,以提升应用程序的性能
  • 内置基于POJO的企业会话管理,适用于web及非web环境
  • 非常简单的API加密
  • 不跟任何框架绑定,可以独立运行

 4、shiro主要的四个组件?

  SecurityManager、Authenticator、Authorizer、Session Manager

5、shiro功能细分为?

  1. Authentication:身份认证/登录(账号密码验证)。
  2. Authorization:授权,即角色或者权限验证。
  3. Session Manager:会话管理,用户登录后的session相关管理。
  4. Cryptography:加密,密码加密等。
  5. Web Support:Web支持,集成Web环境。
  6. Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
  7. Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
  8. Testing:测试支持;
  9. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
  10. Remember Me:记住我,登录后,下次再来的话不用登录了。

6、shiro的四种权限控制方式?

  • url 级别权限控制 
  • 方法注解权限控制 
  • 代码级别权限控制 
  • 页面标签权限控制

7、springboot整合shiro流程?

 

  1,pom.xml文件中添加依赖

<!-- shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${spring.shiro.version}</version>
</dependency>

  

  2,创建对应的用户、角色、权限实体类

 

  3,自定义Realm用于查询用户的角色和权限信息,保存到权限管理器

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

//继承AuthorizingRealm类
public class CustomRealm extends AuthorizingRealm {
  //注入查询数据的service层 @Autowired
private LoginService loginService; /** * @MethodName doGetAuthorizationInfo * @Description 权限配置类 * @Param [principalCollection] * @Return AuthorizationInfo * @Author WangShiLin */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录用户名 String name = (String) principalCollection.getPrimaryPrincipal(); //查询用户名称 User user = loginService.getUserByName(name); //添加角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (Role role : user.getRoles()) { //添加角色 simpleAuthorizationInfo.addRole(role.getRoleName()); //添加权限 for (Permissions permissions : role.getPermissions()) { simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName()); } } return simpleAuthorizationInfo; } /** * @MethodName doGetAuthenticationInfo * @Description 认证配置类 * @Param [authenticationToken] * @Return AuthenticationInfo * @Author WangShiLin */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { if (StringUtils.isEmpty(authenticationToken.getPrincipal())) { return null; } //获取用户信息 String name = authenticationToken.getPrincipal().toString(); User user = loginService.getUserByName(name); if (user == null) { //这里返回后会报出对应异常 return null; } else { //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName()); return simpleAuthenticationInfo; } } }

 

 

  4,新建配置一个配置类,如ShiroConfig.java,把CustomRealm和SecurityManager等注入到spring容器中。

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class shiroConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        //登出
        map.put("/logout", "logout");
        //对所有用户认证
        map.put("/**", "authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

  
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

 

 

  5、控制器类中

    @RequiresPermissions("add"):表示验证权限

    @RequiresRoles("admin"):表示验证角色

 

Lucene全文检索工具包的基本使用

1、什么是全文检索?

  将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,

  从而达到搜索相对较快的目的。这部分从非结构化数据中提取出然后重新组织的信息,我们称之索引。
  这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。

 

2、lucene内部结构是什么?

  索引(Index): 在Lucene中一个索引是放在一个文件夹中的。 如上图,同一文件夹中的所有的文件构成一个Lucene索引。

  段(Segment): 一个索引可以包含多个段,段与段之间是独立的,添加新文档可以生成新的段,不同的段可以合并。

    segments.gen和segments_X是段的元数据文件,也即它们保存了段的属性信息。

  文档(Document): 文档是我们建索引的基本单位,不同的文档是保存在不同的段中的,一个段可以包含多篇文档。

    新添加的文档是单独保存在一个新生成的段中,随着段的合并,不同的文档合并到同一个段中。

  域(Field):

    一篇文档包含不同类型的信息,可以分开索引,比如标题,时间,正文,作者等,都可以保存在不同的域里。 不同域的索引方式可以不同,在真正解析域的存储的时候,我们会详细解读。

  词(Term):

    词是索引的最小单位,是经过词法分析和语言处理后的字符串。

3、IK分词器的原理?

   本质上是词典分词,在内存中初始化一个词典,然后在分词过程中逐个读取字符,和字典中的字符相匹配,

  把文档中的所有词语拆分出来的过程

4、solr和lucene之间的区别?

  lucene是全文检索工具包

  solr是单独运行的全文检索服务器。

 

手写几种排序?

1、冒泡排序

public static void main(String[] args) {
        int arr[] = {8, 5, 3, 2, 4};
        //冒泡
        for (int i = 0; i < arr.length; i++) {
            //外层循环,遍历次数
            for (int j = 0; j < arr.length - i - 1; j++) {
                //内层循环,升序(如果前一个值比后一个值大,则交换)
                //内层循环一次,获取一个最大值
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
    }

 

 

2、选择排序

public static void main(String[] args) {

        int arr[] = {6, 5, 3, 2, 4};

        //选择
        for (int i = 0; i < arr.length; i++) {
            //默认第一个是最小的。
            int min = arr[i];
            //记录最小的下标
            int index = i;
            //通过与后面的数据进行比较得出,最小值和下标
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    index = j;
                }
            }
            //然后将最小值与本次循环的,开始值交换
            int temp = arr[i];
            arr[i] = min;
            arr[index] = temp;
            //说明:将i前面的数据看成一个排好的队列,i后面的看成一个无序队列。每次只需要找无需的最小值,做替换
        }
    }

 

 

3、插入排序

public static void main(String[] args) {

        int arr[] = {7, 5, 3, 2, 4};

        //插入排序
        for (int i = 1; i < arr.length; i++) {
            //外层循环,从第二个开始比较
            for (int j = i; j > 0; j--) {
                //内存循环,与前面排好序的数据比较,如果后面的数据小于前面的则交换
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = temp;
                } else {
                    //如果不小于,说明插入完毕,退出内层循环
                    break;
                }
            }
        }
    }

 

 

4、希尔排序

public static void main(String[] args) {

        int arr[] = {7, 5, 3, 2, 4};

        //希尔排序(插入排序变种版)
        for (int i = arr.length / 2; i > 0; i /= 2) {
            //i层循环控制步长
            for (int j = i; j < arr.length; j++) {
                //j控制无序端的起始位置
                for (int k = j; k > 0  && k - i >= 0; k -= i) {
                    if (arr[k] < arr[k - i]) {
                        int temp = arr[k - i];
                        arr[k - i] = arr[k];
                        arr[k] = temp;
                    } else {
                        break;
                    }
                }
            }
            //j,k为插入排序,不过步长为i
        }
    }

 

 

5、快速排序

public static void main(String[] args) {

        int arr[] = {7, 5, 3, 2, 4, 1, 8, 9, 6};

        //快速排序
        int low = 0;
        int high = arr.length - 1;
        quickSort(arr, low, high);  
    }

    public static void quickSort(int[] arr, int low, int high) {
        //如果指针在同一位置(只有一个数据时),退出
        if (high - low < 1) {
            return;
        }
        //标记,从高指针开始,还是低指针(默认高指针)
        boolean flag = true;
        //记录指针的其实位置
        int start = low;
        int end = high;
        //默认中间值为低指针的第一个值
        int midValue = arr[low];
        while (true) {
            //高指针移动
            if (flag) {
                //如果列表右方的数据大于中间值,则向左移动
                if (arr[high] > midValue) {
                    high--;
                } else if (arr[high] < midValue) {
                    //如果小于,则覆盖最开始的低指针值,并且移动低指针,标志位改成从低指针开始移动
                    arr[low] = arr[high];
                    low++;
                    flag = false;
                }
            } else {
                //如果低指针数据小于中间值,则低指针向右移动
                if (arr[low] < midValue) {
                    low++;
                } else if (arr[low] > midValue) {
                    //如果低指针的值大于中间值,则覆盖高指针停留时的数据,并向左移动高指针。切换为高指针移动
                    arr[high] = arr[low];
                    high--;
                    flag = true;
                }
            }
            //当两个指针的位置相同时,则找到了中间值的位置,并退出循环
            if (low == high) {
                arr[low] = midValue;
                break;
            }
        }
        //然后出现有,中间值左边的小于中间值。右边的大于中间值。
        //然后在对左右两边的列表在进行快速排序
        quickSort(arr, start, low -1);
        quickSort(arr, low + 1, end);
    }

 

 

6、归并排序

public static void main(String[] args) {

        int arr[] = {7, 5, 3, 2, 4, 16};

        //归并排序
        int start = 0;
        int end = arr.length - 1;
        mergeSort(arr, start, end);
    }

    public static void mergeSort(int[] arr, int start, int end) {
        //判断拆分的不为最小单位
        if (end - start > 0) {
            //再一次拆分,知道拆成一个一个的数据
            mergeSort(arr, start, (start + end) / 2);
            mergeSort(arr, (start + end) / 2 + 1, end);
            //记录开始/结束位置
            int left = start;
            int right = (start + end) / 2 + 1;
            //记录每个小单位的排序结果
            int index = 0;
            int[] result = new int[end - start + 1];
            //如果查分后的两块数据,都还存在
            while (left <= (start + end) / 2 && right <= end) {
                //比较两块数据的大小,然后赋值,并且移动下标
                if (arr[left] <= arr[right]) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                //移动单位记录的下标
                index++;
            }
            //当某一块数据不存在了时
            while (left <= (start + end) / 2 || right <= end) {
                //直接赋值到记录下标
                if (left <= (start + end) / 2) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                index++;
            }
            //最后将新的数据赋值给原来的列表,并且是对应分块后的下标。
            for (int i = start; i <= end; i++) {
                arr[i] = result[i - start];
            }
        }
    }

 

 

说说对三个项目的了解

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM