--- a/net/minecraft/core/dispenser/DispenseBehaviorShears.java
+++ b/net/minecraft/core/dispenser/DispenseBehaviorShears.java
@@ -23,6 +23,13 @@
 import net.minecraft.world.level.gameevent.GameEvent;
 import net.minecraft.world.phys.AxisAlignedBB;
 
+// CraftBukkit start
+import org.bukkit.craftbukkit.block.CraftBlock;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.event.block.BlockDispenseEvent;
+// CraftBukkit end
+
 public class DispenseBehaviorShears extends DispenseBehaviorMaybe {
 
     public DispenseBehaviorShears() {}
@@ -30,11 +37,34 @@
     @Override
     protected ItemStack execute(SourceBlock sourceblock, ItemStack itemstack) {
         WorldServer worldserver = sourceblock.level();
+        // CraftBukkit start
+        org.bukkit.block.Block bukkitBlock = CraftBlock.at(worldserver, sourceblock.pos());
+        CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
+
+        BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
+        if (!BlockDispenser.eventFired) {
+            worldserver.getCraftServer().getPluginManager().callEvent(event);
+        }
+
+        if (event.isCancelled()) {
+            return itemstack;
+        }
+
+        if (!event.getItem().equals(craftItem)) {
+            // Chain to handler for new item
+            ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
+            IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.DISPENSER_REGISTRY.get(eventStack.getItem());
+            if (idispensebehavior != IDispenseBehavior.NOOP && idispensebehavior != this) {
+                idispensebehavior.dispense(sourceblock, eventStack);
+                return itemstack;
+            }
+        }
+        // CraftBukkit end
 
         if (!worldserver.isClientSide()) {
             BlockPosition blockposition = sourceblock.pos().relative((EnumDirection) sourceblock.state().getValue(BlockDispenser.FACING));
 
-            this.setSuccess(tryShearBeehive(worldserver, blockposition) || tryShearLivingEntity(worldserver, blockposition, itemstack));
+            this.setSuccess(tryShearBeehive(worldserver, blockposition) || tryShearLivingEntity(worldserver, blockposition, itemstack, bukkitBlock, craftItem)); // CraftBukkit
             if (this.isSuccess()) {
                 itemstack.hurtAndBreak(1, worldserver, (EntityPlayer) null, (item) -> {
                 });
@@ -64,7 +94,7 @@
         return false;
     }
 
-    private static boolean tryShearLivingEntity(WorldServer worldserver, BlockPosition blockposition, ItemStack itemstack) {
+    private static boolean tryShearLivingEntity(WorldServer worldserver, BlockPosition blockposition, ItemStack itemstack, org.bukkit.block.Block bukkitBlock, CraftItemStack craftItem) { // CraftBukkit - add args
         List<EntityLiving> list = worldserver.getEntitiesOfClass(EntityLiving.class, new AxisAlignedBB(blockposition), IEntitySelector.NO_SPECTATORS);
         Iterator iterator = list.iterator();
 
@@ -73,6 +103,11 @@
 
             if (entityliving instanceof IShearable ishearable) {
                 if (ishearable.readyForShearing()) {
+                    // CraftBukkit start
+                    if (CraftEventFactory.callBlockShearEntityEvent(entityliving, bukkitBlock, craftItem).isCancelled()) {
+                        continue;
+                    }
+                    // CraftBukkit end
                     ishearable.shear(worldserver, SoundCategory.BLOCKS, itemstack);
                     worldserver.gameEvent((Entity) null, (Holder) GameEvent.SHEAR, blockposition);
                     return true;
